mirror of https://github.com/FriendsOfTYPO3/tea.git synced 2024-09-20 00:16:12 +02:00
tea/tools/phpcov
Oliver Klee f9daea8344
[TASK] Upgrade to PHPCOV 6 (#540)
PHPCOV 6 is the latest version still supporting PHPUnit 8
(which we're need to use as long as we still support PHP 7.2).
2022-09-03 14:35:31 +02:00

75113 lines
No EOL
2.8 MiB
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env php
<?php
if ($_SERVER['SCRIPT_NAME'] != '-') {
$phar = realpath($_SERVER['SCRIPT_NAME']);
} else {
$files = get_included_files();
$phar = $files[0];
}
define('__PHPCOV_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', $phar));
define('__PHPCOV_PHAR_ROOT__', 'phar://phpcov-6.0.1.phar');
spl_autoload_register(
function ($class)
{
static $classes = NULL;
if ($classes === NULL) {
$classes = array(
'jsonexception' => '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'php_token' => '/phpunit/php-token-stream/src/Token.php',
'php_token_abstract' => '/phpunit/php-token-stream/src/Token.php',
'php_token_ampersand' => '/phpunit/php-token-stream/src/Token.php',
'php_token_and_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_array' => '/phpunit/php-token-stream/src/Token.php',
'php_token_array_cast' => '/phpunit/php-token-stream/src/Token.php',
'php_token_as' => '/phpunit/php-token-stream/src/Token.php',
'php_token_at' => '/phpunit/php-token-stream/src/Token.php',
'php_token_backtick' => '/phpunit/php-token-stream/src/Token.php',
'php_token_bad_character' => '/phpunit/php-token-stream/src/Token.php',
'php_token_bool_cast' => '/phpunit/php-token-stream/src/Token.php',
'php_token_boolean_and' => '/phpunit/php-token-stream/src/Token.php',
'php_token_boolean_or' => '/phpunit/php-token-stream/src/Token.php',
'php_token_break' => '/phpunit/php-token-stream/src/Token.php',
'php_token_callable' => '/phpunit/php-token-stream/src/Token.php',
'php_token_caret' => '/phpunit/php-token-stream/src/Token.php',
'php_token_case' => '/phpunit/php-token-stream/src/Token.php',
'php_token_catch' => '/phpunit/php-token-stream/src/Token.php',
'php_token_character' => '/phpunit/php-token-stream/src/Token.php',
'php_token_class' => '/phpunit/php-token-stream/src/Token.php',
'php_token_class_c' => '/phpunit/php-token-stream/src/Token.php',
'php_token_class_name_constant' => '/phpunit/php-token-stream/src/Token.php',
'php_token_clone' => '/phpunit/php-token-stream/src/Token.php',
'php_token_close_bracket' => '/phpunit/php-token-stream/src/Token.php',
'php_token_close_curly' => '/phpunit/php-token-stream/src/Token.php',
'php_token_close_square' => '/phpunit/php-token-stream/src/Token.php',
'php_token_close_tag' => '/phpunit/php-token-stream/src/Token.php',
'php_token_coalesce' => '/phpunit/php-token-stream/src/Token.php',
'php_token_coalesce_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_colon' => '/phpunit/php-token-stream/src/Token.php',
'php_token_comma' => '/phpunit/php-token-stream/src/Token.php',
'php_token_comment' => '/phpunit/php-token-stream/src/Token.php',
'php_token_concat_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_const' => '/phpunit/php-token-stream/src/Token.php',
'php_token_constant_encapsed_string' => '/phpunit/php-token-stream/src/Token.php',
'php_token_continue' => '/phpunit/php-token-stream/src/Token.php',
'php_token_curly_open' => '/phpunit/php-token-stream/src/Token.php',
'php_token_dec' => '/phpunit/php-token-stream/src/Token.php',
'php_token_declare' => '/phpunit/php-token-stream/src/Token.php',
'php_token_default' => '/phpunit/php-token-stream/src/Token.php',
'php_token_dir' => '/phpunit/php-token-stream/src/Token.php',
'php_token_div' => '/phpunit/php-token-stream/src/Token.php',
'php_token_div_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_dnumber' => '/phpunit/php-token-stream/src/Token.php',
'php_token_do' => '/phpunit/php-token-stream/src/Token.php',
'php_token_doc_comment' => '/phpunit/php-token-stream/src/Token.php',
'php_token_dollar' => '/phpunit/php-token-stream/src/Token.php',
'php_token_dollar_open_curly_braces' => '/phpunit/php-token-stream/src/Token.php',
'php_token_dot' => '/phpunit/php-token-stream/src/Token.php',
'php_token_double_arrow' => '/phpunit/php-token-stream/src/Token.php',
'php_token_double_cast' => '/phpunit/php-token-stream/src/Token.php',
'php_token_double_colon' => '/phpunit/php-token-stream/src/Token.php',
'php_token_double_quotes' => '/phpunit/php-token-stream/src/Token.php',
'php_token_echo' => '/phpunit/php-token-stream/src/Token.php',
'php_token_ellipsis' => '/phpunit/php-token-stream/src/Token.php',
'php_token_else' => '/phpunit/php-token-stream/src/Token.php',
'php_token_elseif' => '/phpunit/php-token-stream/src/Token.php',
'php_token_empty' => '/phpunit/php-token-stream/src/Token.php',
'php_token_encapsed_and_whitespace' => '/phpunit/php-token-stream/src/Token.php',
'php_token_end_heredoc' => '/phpunit/php-token-stream/src/Token.php',
'php_token_enddeclare' => '/phpunit/php-token-stream/src/Token.php',
'php_token_endfor' => '/phpunit/php-token-stream/src/Token.php',
'php_token_endforeach' => '/phpunit/php-token-stream/src/Token.php',
'php_token_endif' => '/phpunit/php-token-stream/src/Token.php',
'php_token_endswitch' => '/phpunit/php-token-stream/src/Token.php',
'php_token_endwhile' => '/phpunit/php-token-stream/src/Token.php',
'php_token_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_eval' => '/phpunit/php-token-stream/src/Token.php',
'php_token_exclamation_mark' => '/phpunit/php-token-stream/src/Token.php',
'php_token_exit' => '/phpunit/php-token-stream/src/Token.php',
'php_token_extends' => '/phpunit/php-token-stream/src/Token.php',
'php_token_file' => '/phpunit/php-token-stream/src/Token.php',
'php_token_final' => '/phpunit/php-token-stream/src/Token.php',
'php_token_finally' => '/phpunit/php-token-stream/src/Token.php',
'php_token_fn' => '/phpunit/php-token-stream/src/Token.php',
'php_token_for' => '/phpunit/php-token-stream/src/Token.php',
'php_token_foreach' => '/phpunit/php-token-stream/src/Token.php',
'php_token_func_c' => '/phpunit/php-token-stream/src/Token.php',
'php_token_function' => '/phpunit/php-token-stream/src/Token.php',
'php_token_global' => '/phpunit/php-token-stream/src/Token.php',
'php_token_goto' => '/phpunit/php-token-stream/src/Token.php',
'php_token_gt' => '/phpunit/php-token-stream/src/Token.php',
'php_token_halt_compiler' => '/phpunit/php-token-stream/src/Token.php',
'php_token_if' => '/phpunit/php-token-stream/src/Token.php',
'php_token_implements' => '/phpunit/php-token-stream/src/Token.php',
'php_token_inc' => '/phpunit/php-token-stream/src/Token.php',
'php_token_include' => '/phpunit/php-token-stream/src/Token.php',
'php_token_include_once' => '/phpunit/php-token-stream/src/Token.php',
'php_token_includes' => '/phpunit/php-token-stream/src/Token.php',
'php_token_inline_html' => '/phpunit/php-token-stream/src/Token.php',
'php_token_instanceof' => '/phpunit/php-token-stream/src/Token.php',
'php_token_insteadof' => '/phpunit/php-token-stream/src/Token.php',
'php_token_int_cast' => '/phpunit/php-token-stream/src/Token.php',
'php_token_interface' => '/phpunit/php-token-stream/src/Token.php',
'php_token_is_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_is_greater_or_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_is_identical' => '/phpunit/php-token-stream/src/Token.php',
'php_token_is_not_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_is_not_identical' => '/phpunit/php-token-stream/src/Token.php',
'php_token_is_smaller_or_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_isset' => '/phpunit/php-token-stream/src/Token.php',
'php_token_line' => '/phpunit/php-token-stream/src/Token.php',
'php_token_list' => '/phpunit/php-token-stream/src/Token.php',
'php_token_lnumber' => '/phpunit/php-token-stream/src/Token.php',
'php_token_logical_and' => '/phpunit/php-token-stream/src/Token.php',
'php_token_logical_or' => '/phpunit/php-token-stream/src/Token.php',
'php_token_logical_xor' => '/phpunit/php-token-stream/src/Token.php',
'php_token_lt' => '/phpunit/php-token-stream/src/Token.php',
'php_token_method_c' => '/phpunit/php-token-stream/src/Token.php',
'php_token_minus' => '/phpunit/php-token-stream/src/Token.php',
'php_token_minus_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_mod_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_mul_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_mult' => '/phpunit/php-token-stream/src/Token.php',
'php_token_namespace' => '/phpunit/php-token-stream/src/Token.php',
'php_token_new' => '/phpunit/php-token-stream/src/Token.php',
'php_token_ns_c' => '/phpunit/php-token-stream/src/Token.php',
'php_token_ns_separator' => '/phpunit/php-token-stream/src/Token.php',
'php_token_num_string' => '/phpunit/php-token-stream/src/Token.php',
'php_token_object_cast' => '/phpunit/php-token-stream/src/Token.php',
'php_token_object_operator' => '/phpunit/php-token-stream/src/Token.php',
'php_token_open_bracket' => '/phpunit/php-token-stream/src/Token.php',
'php_token_open_curly' => '/phpunit/php-token-stream/src/Token.php',
'php_token_open_square' => '/phpunit/php-token-stream/src/Token.php',
'php_token_open_tag' => '/phpunit/php-token-stream/src/Token.php',
'php_token_open_tag_with_echo' => '/phpunit/php-token-stream/src/Token.php',
'php_token_or_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_paamayim_nekudotayim' => '/phpunit/php-token-stream/src/Token.php',
'php_token_percent' => '/phpunit/php-token-stream/src/Token.php',
'php_token_pipe' => '/phpunit/php-token-stream/src/Token.php',
'php_token_plus' => '/phpunit/php-token-stream/src/Token.php',
'php_token_plus_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_pow' => '/phpunit/php-token-stream/src/Token.php',
'php_token_pow_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_print' => '/phpunit/php-token-stream/src/Token.php',
'php_token_private' => '/phpunit/php-token-stream/src/Token.php',
'php_token_protected' => '/phpunit/php-token-stream/src/Token.php',
'php_token_public' => '/phpunit/php-token-stream/src/Token.php',
'php_token_question_mark' => '/phpunit/php-token-stream/src/Token.php',
'php_token_require' => '/phpunit/php-token-stream/src/Token.php',
'php_token_require_once' => '/phpunit/php-token-stream/src/Token.php',
'php_token_return' => '/phpunit/php-token-stream/src/Token.php',
'php_token_semicolon' => '/phpunit/php-token-stream/src/Token.php',
'php_token_sl' => '/phpunit/php-token-stream/src/Token.php',
'php_token_sl_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_spaceship' => '/phpunit/php-token-stream/src/Token.php',
'php_token_sr' => '/phpunit/php-token-stream/src/Token.php',
'php_token_sr_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_start_heredoc' => '/phpunit/php-token-stream/src/Token.php',
'php_token_static' => '/phpunit/php-token-stream/src/Token.php',
'php_token_stream' => '/phpunit/php-token-stream/src/Token/Stream.php',
'php_token_stream_cachingfactory' => '/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php',
'php_token_string' => '/phpunit/php-token-stream/src/Token.php',
'php_token_string_cast' => '/phpunit/php-token-stream/src/Token.php',
'php_token_string_varname' => '/phpunit/php-token-stream/src/Token.php',
'php_token_switch' => '/phpunit/php-token-stream/src/Token.php',
'php_token_throw' => '/phpunit/php-token-stream/src/Token.php',
'php_token_tilde' => '/phpunit/php-token-stream/src/Token.php',
'php_token_trait' => '/phpunit/php-token-stream/src/Token.php',
'php_token_trait_c' => '/phpunit/php-token-stream/src/Token.php',
'php_token_try' => '/phpunit/php-token-stream/src/Token.php',
'php_token_unset' => '/phpunit/php-token-stream/src/Token.php',
'php_token_unset_cast' => '/phpunit/php-token-stream/src/Token.php',
'php_token_use' => '/phpunit/php-token-stream/src/Token.php',
'php_token_use_function' => '/phpunit/php-token-stream/src/Token.php',
'php_token_util' => '/phpunit/php-token-stream/src/Token/Util.php',
'php_token_var' => '/phpunit/php-token-stream/src/Token.php',
'php_token_variable' => '/phpunit/php-token-stream/src/Token.php',
'php_token_while' => '/phpunit/php-token-stream/src/Token.php',
'php_token_whitespace' => '/phpunit/php-token-stream/src/Token.php',
'php_token_xor_equal' => '/phpunit/php-token-stream/src/Token.php',
'php_token_yield' => '/phpunit/php-token-stream/src/Token.php',
'php_token_yield_from' => '/phpunit/php-token-stream/src/Token.php',
'php_tokenwithscope' => '/phpunit/php-token-stream/src/Token.php',
'php_tokenwithscopeandvisibility' => '/phpunit/php-token-stream/src/Token.php',
'phpunit\\exception' => '/phpunit/phpunit/src/Exception.php',
'phpunit\\framework\\assert' => '/phpunit/phpunit/src/Framework/Assert.php',
'phpunit\\framework\\assertionfailederror' => '/phpunit/phpunit/src/Framework/Exception/AssertionFailedError.php',
'phpunit\\framework\\codecoverageexception' => '/phpunit/phpunit/src/Framework/Exception/CodeCoverageException.php',
'phpunit\\framework\\constraint\\arrayhaskey' => '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php',
'phpunit\\framework\\constraint\\arraysubset' => '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php',
'phpunit\\framework\\constraint\\attribute' => '/phpunit/phpunit/src/Framework/Constraint/Attribute.php',
'phpunit\\framework\\constraint\\callback' => '/phpunit/phpunit/src/Framework/Constraint/Callback.php',
'phpunit\\framework\\constraint\\classhasattribute' => '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php',
'phpunit\\framework\\constraint\\classhasstaticattribute' => '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php',
'phpunit\\framework\\constraint\\composite' => '/phpunit/phpunit/src/Framework/Constraint/Composite.php',
'phpunit\\framework\\constraint\\constraint' => '/phpunit/phpunit/src/Framework/Constraint/Constraint.php',
'phpunit\\framework\\constraint\\count' => '/phpunit/phpunit/src/Framework/Constraint/Count.php',
'phpunit\\framework\\constraint\\directoryexists' => '/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php',
'phpunit\\framework\\constraint\\exception' => '/phpunit/phpunit/src/Framework/Constraint/Exception.php',
'phpunit\\framework\\constraint\\exceptioncode' => '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php',
'phpunit\\framework\\constraint\\exceptionmessage' => '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php',
'phpunit\\framework\\constraint\\exceptionmessageregularexpression' => '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegularExpression.php',
'phpunit\\framework\\constraint\\fileexists' => '/phpunit/phpunit/src/Framework/Constraint/FileExists.php',
'phpunit\\framework\\constraint\\greaterthan' => '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php',
'phpunit\\framework\\constraint\\isanything' => '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php',
'phpunit\\framework\\constraint\\isempty' => '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php',
'phpunit\\framework\\constraint\\isequal' => '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php',
'phpunit\\framework\\constraint\\isfalse' => '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php',
'phpunit\\framework\\constraint\\isfinite' => '/phpunit/phpunit/src/Framework/Constraint/IsFinite.php',
'phpunit\\framework\\constraint\\isidentical' => '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php',
'phpunit\\framework\\constraint\\isinfinite' => '/phpunit/phpunit/src/Framework/Constraint/IsInfinite.php',
'phpunit\\framework\\constraint\\isinstanceof' => '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php',
'phpunit\\framework\\constraint\\isjson' => '/phpunit/phpunit/src/Framework/Constraint/IsJson.php',
'phpunit\\framework\\constraint\\isnan' => '/phpunit/phpunit/src/Framework/Constraint/IsNan.php',
'phpunit\\framework\\constraint\\isnull' => '/phpunit/phpunit/src/Framework/Constraint/IsNull.php',
'phpunit\\framework\\constraint\\isreadable' => '/phpunit/phpunit/src/Framework/Constraint/IsReadable.php',
'phpunit\\framework\\constraint\\istrue' => '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php',
'phpunit\\framework\\constraint\\istype' => '/phpunit/phpunit/src/Framework/Constraint/IsType.php',
'phpunit\\framework\\constraint\\iswritable' => '/phpunit/phpunit/src/Framework/Constraint/IsWritable.php',
'phpunit\\framework\\constraint\\jsonmatches' => '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php',
'phpunit\\framework\\constraint\\jsonmatcheserrormessageprovider' => '/phpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php',
'phpunit\\framework\\constraint\\lessthan' => '/phpunit/phpunit/src/Framework/Constraint/LessThan.php',
'phpunit\\framework\\constraint\\logicaland' => '/phpunit/phpunit/src/Framework/Constraint/LogicalAnd.php',
'phpunit\\framework\\constraint\\logicalnot' => '/phpunit/phpunit/src/Framework/Constraint/LogicalNot.php',
'phpunit\\framework\\constraint\\logicalor' => '/phpunit/phpunit/src/Framework/Constraint/LogicalOr.php',
'phpunit\\framework\\constraint\\logicalxor' => '/phpunit/phpunit/src/Framework/Constraint/LogicalXor.php',
'phpunit\\framework\\constraint\\objecthasattribute' => '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php',
'phpunit\\framework\\constraint\\regularexpression' => '/phpunit/phpunit/src/Framework/Constraint/RegularExpression.php',
'phpunit\\framework\\constraint\\samesize' => '/phpunit/phpunit/src/Framework/Constraint/SameSize.php',
'phpunit\\framework\\constraint\\stringcontains' => '/phpunit/phpunit/src/Framework/Constraint/StringContains.php',
'phpunit\\framework\\constraint\\stringendswith' => '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php',
'phpunit\\framework\\constraint\\stringmatchesformatdescription' => '/phpunit/phpunit/src/Framework/Constraint/StringMatchesFormatDescription.php',
'phpunit\\framework\\constraint\\stringstartswith' => '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php',
'phpunit\\framework\\constraint\\traversablecontains' => '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php',
'phpunit\\framework\\constraint\\traversablecontainsonly' => '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php',
'phpunit\\framework\\coveredcodenotexecutedexception' => '/phpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php',
'phpunit\\framework\\dataprovidertestsuite' => '/phpunit/phpunit/src/Framework/DataProviderTestSuite.php',
'phpunit\\framework\\error\\deprecated' => '/phpunit/phpunit/src/Framework/Error/Deprecated.php',
'phpunit\\framework\\error\\error' => '/phpunit/phpunit/src/Framework/Error/Error.php',
'phpunit\\framework\\error\\notice' => '/phpunit/phpunit/src/Framework/Error/Notice.php',
'phpunit\\framework\\error\\warning' => '/phpunit/phpunit/src/Framework/Error/Warning.php',
'phpunit\\framework\\exception' => '/phpunit/phpunit/src/Framework/Exception/Exception.php',
'phpunit\\framework\\exceptionwrapper' => '/phpunit/phpunit/src/Framework/ExceptionWrapper.php',
'phpunit\\framework\\expectationfailedexception' => '/phpunit/phpunit/src/Framework/Exception/ExpectationFailedException.php',
'phpunit\\framework\\incompletetest' => '/phpunit/phpunit/src/Framework/IncompleteTest.php',
'phpunit\\framework\\incompletetestcase' => '/phpunit/phpunit/src/Framework/IncompleteTestCase.php',
'phpunit\\framework\\incompletetesterror' => '/phpunit/phpunit/src/Framework/Exception/IncompleteTestError.php',
'phpunit\\framework\\invalidargumentexception' => '/phpunit/phpunit/src/Framework/Exception/InvalidArgumentException.php',
'phpunit\\framework\\invalidcoverstargetexception' => '/phpunit/phpunit/src/Framework/Exception/InvalidCoversTargetException.php',
'phpunit\\framework\\invaliddataproviderexception' => '/phpunit/phpunit/src/Framework/Exception/InvalidDataProviderException.php',
'phpunit\\framework\\invalidparametergroupexception' => '/phpunit/phpunit/src/Framework/InvalidParameterGroupException.php',
'phpunit\\framework\\missingcoversannotationexception' => '/phpunit/phpunit/src/Framework/Exception/MissingCoversAnnotationException.php',
'phpunit\\framework\\mockobject\\api' => '/phpunit/phpunit/src/Framework/MockObject/Api/Api.php',
'phpunit\\framework\\mockobject\\badmethodcallexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/BadMethodCallException.php',
'phpunit\\framework\\mockobject\\builder\\identity' => '/phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php',
'phpunit\\framework\\mockobject\\builder\\invocationmocker' => '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php',
'phpunit\\framework\\mockobject\\builder\\invocationstubber' => '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php',
'phpunit\\framework\\mockobject\\builder\\match' => '/phpunit/phpunit/src/Framework/MockObject/Builder/Match.php',
'phpunit\\framework\\mockobject\\builder\\methodnamematch' => '/phpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.php',
'phpunit\\framework\\mockobject\\builder\\parametersmatch' => '/phpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php',
'phpunit\\framework\\mockobject\\builder\\stub' => '/phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php',
'phpunit\\framework\\mockobject\\configurablemethod' => '/phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php',
'phpunit\\framework\\mockobject\\configurablemethodsalreadyinitializedexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php',
'phpunit\\framework\\mockobject\\exception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php',
'phpunit\\framework\\mockobject\\generator' => '/phpunit/phpunit/src/Framework/MockObject/Generator.php',
'phpunit\\framework\\mockobject\\incompatiblereturnvalueexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php',
'phpunit\\framework\\mockobject\\invocation' => '/phpunit/phpunit/src/Framework/MockObject/Invocation.php',
'phpunit\\framework\\mockobject\\invocationhandler' => '/phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php',
'phpunit\\framework\\mockobject\\matcher' => '/phpunit/phpunit/src/Framework/MockObject/Matcher.php',
'phpunit\\framework\\mockobject\\method' => '/phpunit/phpunit/src/Framework/MockObject/Api/Method.php',
'phpunit\\framework\\mockobject\\methodnameconstraint' => '/phpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php',
'phpunit\\framework\\mockobject\\mockbuilder' => '/phpunit/phpunit/src/Framework/MockObject/MockBuilder.php',
'phpunit\\framework\\mockobject\\mockclass' => '/phpunit/phpunit/src/Framework/MockObject/MockClass.php',
'phpunit\\framework\\mockobject\\mockedclonemethod' => '/phpunit/phpunit/src/Framework/MockObject/Api/MockedCloneMethod.php',
'phpunit\\framework\\mockobject\\mockmethod' => '/phpunit/phpunit/src/Framework/MockObject/MockMethod.php',
'phpunit\\framework\\mockobject\\mockmethodset' => '/phpunit/phpunit/src/Framework/MockObject/MockMethodSet.php',
'phpunit\\framework\\mockobject\\mockobject' => '/phpunit/phpunit/src/Framework/MockObject/MockObject.php',
'phpunit\\framework\\mockobject\\mocktrait' => '/phpunit/phpunit/src/Framework/MockObject/MockTrait.php',
'phpunit\\framework\\mockobject\\mocktype' => '/phpunit/phpunit/src/Framework/MockObject/MockType.php',
'phpunit\\framework\\mockobject\\rule\\anyinvokedcount' => '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.php',
'phpunit\\framework\\mockobject\\rule\\anyparameters' => '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.php',
'phpunit\\framework\\mockobject\\rule\\consecutiveparameters' => '/phpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php',
'phpunit\\framework\\mockobject\\rule\\invocationorder' => '/phpunit/phpunit/src/Framework/MockObject/Rule/InvocationOrder.php',
'phpunit\\framework\\mockobject\\rule\\invokedatindex' => '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtIndex.php',
'phpunit\\framework\\mockobject\\rule\\invokedatleastcount' => '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastCount.php',
'phpunit\\framework\\mockobject\\rule\\invokedatleastonce' => '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastOnce.php',
'phpunit\\framework\\mockobject\\rule\\invokedatmostcount' => '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtMostCount.php',
'phpunit\\framework\\mockobject\\rule\\invokedcount' => '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedCount.php',
'phpunit\\framework\\mockobject\\rule\\methodname' => '/phpunit/phpunit/src/Framework/MockObject/Rule/MethodName.php',
'phpunit\\framework\\mockobject\\rule\\parameters' => '/phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.php',
'phpunit\\framework\\mockobject\\rule\\parametersrule' => '/phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php',
'phpunit\\framework\\mockobject\\runtimeexception' => '/phpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php',
'phpunit\\framework\\mockobject\\stub' => '/phpunit/phpunit/src/Framework/MockObject/Stub.php',
'phpunit\\framework\\mockobject\\stub\\consecutivecalls' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php',
'phpunit\\framework\\mockobject\\stub\\exception' => '/phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php',
'phpunit\\framework\\mockobject\\stub\\returnargument' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnArgument.php',
'phpunit\\framework\\mockobject\\stub\\returncallback' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnCallback.php',
'phpunit\\framework\\mockobject\\stub\\returnreference' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnReference.php',
'phpunit\\framework\\mockobject\\stub\\returnself' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnSelf.php',
'phpunit\\framework\\mockobject\\stub\\returnstub' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php',
'phpunit\\framework\\mockobject\\stub\\returnvaluemap' => '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php',
'phpunit\\framework\\mockobject\\stub\\stub' => '/phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php',
'phpunit\\framework\\mockobject\\unmockedclonemethod' => '/phpunit/phpunit/src/Framework/MockObject/Api/UnmockedCloneMethod.php',
'phpunit\\framework\\mockobject\\verifiable' => '/phpunit/phpunit/src/Framework/MockObject/Verifiable.php',
'phpunit\\framework\\outputerror' => '/phpunit/phpunit/src/Framework/Exception/OutputError.php',
'phpunit\\framework\\phptassertionfailederror' => '/phpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php',
'phpunit\\framework\\riskytesterror' => '/phpunit/phpunit/src/Framework/Exception/RiskyTestError.php',
'phpunit\\framework\\selfdescribing' => '/phpunit/phpunit/src/Framework/SelfDescribing.php',
'phpunit\\framework\\skippedtest' => '/phpunit/phpunit/src/Framework/SkippedTest.php',
'phpunit\\framework\\skippedtestcase' => '/phpunit/phpunit/src/Framework/SkippedTestCase.php',
'phpunit\\framework\\skippedtesterror' => '/phpunit/phpunit/src/Framework/Exception/SkippedTestError.php',
'phpunit\\framework\\skippedtestsuiteerror' => '/phpunit/phpunit/src/Framework/Exception/SkippedTestSuiteError.php',
'phpunit\\framework\\syntheticerror' => '/phpunit/phpunit/src/Framework/Exception/SyntheticError.php',
'phpunit\\framework\\syntheticskippederror' => '/phpunit/phpunit/src/Framework/Exception/SyntheticSkippedError.php',
'phpunit\\framework\\test' => '/phpunit/phpunit/src/Framework/Test.php',
'phpunit\\framework\\testbuilder' => '/phpunit/phpunit/src/Framework/TestBuilder.php',
'phpunit\\framework\\testcase' => '/phpunit/phpunit/src/Framework/TestCase.php',
'phpunit\\framework\\testfailure' => '/phpunit/phpunit/src/Framework/TestFailure.php',
'phpunit\\framework\\testlistener' => '/phpunit/phpunit/src/Framework/TestListener.php',
'phpunit\\framework\\testlistenerdefaultimplementation' => '/phpunit/phpunit/src/Framework/TestListenerDefaultImplementation.php',
'phpunit\\framework\\testresult' => '/phpunit/phpunit/src/Framework/TestResult.php',
'phpunit\\framework\\testsuite' => '/phpunit/phpunit/src/Framework/TestSuite.php',
'phpunit\\framework\\testsuiteiterator' => '/phpunit/phpunit/src/Framework/TestSuiteIterator.php',
'phpunit\\framework\\unexpectedvalueexception' => '/phpunit/phpunit/src/Framework/Exception/UnexpectedValueException.php',
'phpunit\\framework\\unintentionallycoveredcodeerror' => '/phpunit/phpunit/src/Framework/Exception/UnintentionallyCoveredCodeError.php',
'phpunit\\framework\\warning' => '/phpunit/phpunit/src/Framework/Exception/Warning.php',
'phpunit\\framework\\warningtestcase' => '/phpunit/phpunit/src/Framework/WarningTestCase.php',
'phpunit\\runner\\afterincompletetesthook' => '/phpunit/phpunit/src/Runner/Hook/AfterIncompleteTestHook.php',
'phpunit\\runner\\afterlasttesthook' => '/phpunit/phpunit/src/Runner/Hook/AfterLastTestHook.php',
'phpunit\\runner\\afterriskytesthook' => '/phpunit/phpunit/src/Runner/Hook/AfterRiskyTestHook.php',
'phpunit\\runner\\afterskippedtesthook' => '/phpunit/phpunit/src/Runner/Hook/AfterSkippedTestHook.php',
'phpunit\\runner\\aftersuccessfultesthook' => '/phpunit/phpunit/src/Runner/Hook/AfterSuccessfulTestHook.php',
'phpunit\\runner\\aftertesterrorhook' => '/phpunit/phpunit/src/Runner/Hook/AfterTestErrorHook.php',
'phpunit\\runner\\aftertestfailurehook' => '/phpunit/phpunit/src/Runner/Hook/AfterTestFailureHook.php',
'phpunit\\runner\\aftertesthook' => '/phpunit/phpunit/src/Runner/Hook/AfterTestHook.php',
'phpunit\\runner\\aftertestwarninghook' => '/phpunit/phpunit/src/Runner/Hook/AfterTestWarningHook.php',
'phpunit\\runner\\basetestrunner' => '/phpunit/phpunit/src/Runner/BaseTestRunner.php',
'phpunit\\runner\\beforefirsttesthook' => '/phpunit/phpunit/src/Runner/Hook/BeforeFirstTestHook.php',
'phpunit\\runner\\beforetesthook' => '/phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php',
'phpunit\\runner\\defaulttestresultcache' => '/phpunit/phpunit/src/Runner/DefaultTestResultCache.php',
'phpunit\\runner\\exception' => '/phpunit/phpunit/src/Runner/Exception.php',
'phpunit\\runner\\filter\\excludegroupfilteriterator' => '/phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.php',
'phpunit\\runner\\filter\\factory' => '/phpunit/phpunit/src/Runner/Filter/Factory.php',
'phpunit\\runner\\filter\\groupfilteriterator' => '/phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php',
'phpunit\\runner\\filter\\includegroupfilteriterator' => '/phpunit/phpunit/src/Runner/Filter/IncludeGroupFilterIterator.php',
'phpunit\\runner\\filter\\namefilteriterator' => '/phpunit/phpunit/src/Runner/Filter/NameFilterIterator.php',
'phpunit\\runner\\hook' => '/phpunit/phpunit/src/Runner/Hook/Hook.php',
'phpunit\\runner\\nulltestresultcache' => '/phpunit/phpunit/src/Runner/NullTestResultCache.php',
'phpunit\\runner\\phpttestcase' => '/phpunit/phpunit/src/Runner/PhptTestCase.php',
'phpunit\\runner\\resultcacheextension' => '/phpunit/phpunit/src/Runner/ResultCacheExtension.php',
'phpunit\\runner\\standardtestsuiteloader' => '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php',
'phpunit\\runner\\testhook' => '/phpunit/phpunit/src/Runner/Hook/TestHook.php',
'phpunit\\runner\\testlisteneradapter' => '/phpunit/phpunit/src/Runner/Hook/TestListenerAdapter.php',
'phpunit\\runner\\testresultcache' => '/phpunit/phpunit/src/Runner/TestResultCache.php',
'phpunit\\runner\\testsuiteloader' => '/phpunit/phpunit/src/Runner/TestSuiteLoader.php',
'phpunit\\runner\\testsuitesorter' => '/phpunit/phpunit/src/Runner/TestSuiteSorter.php',
'phpunit\\runner\\version' => '/phpunit/phpunit/src/Runner/Version.php',
'phpunit\\textui\\command' => '/phpunit/phpunit/src/TextUI/Command.php',
'phpunit\\textui\\exception' => '/phpunit/phpunit/src/TextUI/Exception.php',
'phpunit\\textui\\help' => '/phpunit/phpunit/src/TextUI/Help.php',
'phpunit\\textui\\resultprinter' => '/phpunit/phpunit/src/TextUI/ResultPrinter.php',
'phpunit\\textui\\testrunner' => '/phpunit/phpunit/src/TextUI/TestRunner.php',
'phpunit\\util\\annotation\\docblock' => '/phpunit/phpunit/src/Util/Annotation/DocBlock.php',
'phpunit\\util\\annotation\\registry' => '/phpunit/phpunit/src/Util/Annotation/Registry.php',
'phpunit\\util\\blacklist' => '/phpunit/phpunit/src/Util/Blacklist.php',
'phpunit\\util\\color' => '/phpunit/phpunit/src/Util/Color.php',
'phpunit\\util\\configuration' => '/phpunit/phpunit/src/Util/Configuration.php',
'phpunit\\util\\configurationgenerator' => '/phpunit/phpunit/src/Util/ConfigurationGenerator.php',
'phpunit\\util\\errorhandler' => '/phpunit/phpunit/src/Util/ErrorHandler.php',
'phpunit\\util\\exception' => '/phpunit/phpunit/src/Util/Exception.php',
'phpunit\\util\\fileloader' => '/phpunit/phpunit/src/Util/FileLoader.php',
'phpunit\\util\\filesystem' => '/phpunit/phpunit/src/Util/Filesystem.php',
'phpunit\\util\\filter' => '/phpunit/phpunit/src/Util/Filter.php',
'phpunit\\util\\getopt' => '/phpunit/phpunit/src/Util/Getopt.php',
'phpunit\\util\\globalstate' => '/phpunit/phpunit/src/Util/GlobalState.php',
'phpunit\\util\\json' => '/phpunit/phpunit/src/Util/Json.php',
'phpunit\\util\\log\\junit' => '/phpunit/phpunit/src/Util/Log/JUnit.php',
'phpunit\\util\\log\\teamcity' => '/phpunit/phpunit/src/Util/Log/TeamCity.php',
'phpunit\\util\\php\\abstractphpprocess' => '/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php',
'phpunit\\util\\php\\defaultphpprocess' => '/phpunit/phpunit/src/Util/PHP/DefaultPhpProcess.php',
'phpunit\\util\\php\\windowsphpprocess' => '/phpunit/phpunit/src/Util/PHP/WindowsPhpProcess.php',
'phpunit\\util\\printer' => '/phpunit/phpunit/src/Util/Printer.php',
'phpunit\\util\\regularexpression' => '/phpunit/phpunit/src/Util/RegularExpression.php',
'phpunit\\util\\test' => '/phpunit/phpunit/src/Util/Test.php',
'phpunit\\util\\testdox\\clitestdoxprinter' => '/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.php',
'phpunit\\util\\testdox\\htmlresultprinter' => '/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php',
'phpunit\\util\\testdox\\nameprettifier' => '/phpunit/phpunit/src/Util/TestDox/NamePrettifier.php',
'phpunit\\util\\testdox\\resultprinter' => '/phpunit/phpunit/src/Util/TestDox/ResultPrinter.php',
'phpunit\\util\\testdox\\testdoxprinter' => '/phpunit/phpunit/src/Util/TestDox/TestDoxPrinter.php',
'phpunit\\util\\testdox\\textresultprinter' => '/phpunit/phpunit/src/Util/TestDox/TextResultPrinter.php',
'phpunit\\util\\testdox\\xmlresultprinter' => '/phpunit/phpunit/src/Util/TestDox/XmlResultPrinter.php',
'phpunit\\util\\texttestlistrenderer' => '/phpunit/phpunit/src/Util/TextTestListRenderer.php',
'phpunit\\util\\type' => '/phpunit/phpunit/src/Util/Type.php',
'phpunit\\util\\xdebugfilterscriptgenerator' => '/phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php',
'phpunit\\util\\xml' => '/phpunit/phpunit/src/Util/Xml.php',
'phpunit\\util\\xmltestlistrenderer' => '/phpunit/phpunit/src/Util/XmlTestListRenderer.php',
'sebastianbergmann\\codecoverage\\codecoverage' => '/phpunit/php-code-coverage/src/CodeCoverage.php',
'sebastianbergmann\\codecoverage\\coveredcodenotexecutedexception' => '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php',
'sebastianbergmann\\codecoverage\\driver\\driver' => '/phpunit/php-code-coverage/src/Driver/Driver.php',
'sebastianbergmann\\codecoverage\\driver\\pcov' => '/phpunit/php-code-coverage/src/Driver/PCOV.php',
'sebastianbergmann\\codecoverage\\driver\\phpdbg' => '/phpunit/php-code-coverage/src/Driver/PHPDBG.php',
'sebastianbergmann\\codecoverage\\driver\\xdebug' => '/phpunit/php-code-coverage/src/Driver/Xdebug.php',
'sebastianbergmann\\codecoverage\\exception' => '/phpunit/php-code-coverage/src/Exception/Exception.php',
'sebastianbergmann\\codecoverage\\filter' => '/phpunit/php-code-coverage/src/Filter.php',
'sebastianbergmann\\codecoverage\\invalidargumentexception' => '/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php',
'sebastianbergmann\\codecoverage\\missingcoversannotationexception' => '/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php',
'sebastianbergmann\\codecoverage\\node\\abstractnode' => '/phpunit/php-code-coverage/src/Node/AbstractNode.php',
'sebastianbergmann\\codecoverage\\node\\builder' => '/phpunit/php-code-coverage/src/Node/Builder.php',
'sebastianbergmann\\codecoverage\\node\\directory' => '/phpunit/php-code-coverage/src/Node/Directory.php',
'sebastianbergmann\\codecoverage\\node\\file' => '/phpunit/php-code-coverage/src/Node/File.php',
'sebastianbergmann\\codecoverage\\node\\iterator' => '/phpunit/php-code-coverage/src/Node/Iterator.php',
'sebastianbergmann\\codecoverage\\report\\clover' => '/phpunit/php-code-coverage/src/Report/Clover.php',
'sebastianbergmann\\codecoverage\\report\\crap4j' => '/phpunit/php-code-coverage/src/Report/Crap4j.php',
'sebastianbergmann\\codecoverage\\report\\html\\dashboard' => '/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php',
'sebastianbergmann\\codecoverage\\report\\html\\directory' => '/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php',
'sebastianbergmann\\codecoverage\\report\\html\\facade' => '/phpunit/php-code-coverage/src/Report/Html/Facade.php',
'sebastianbergmann\\codecoverage\\report\\html\\file' => '/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php',
'sebastianbergmann\\codecoverage\\report\\html\\renderer' => '/phpunit/php-code-coverage/src/Report/Html/Renderer.php',
'sebastianbergmann\\codecoverage\\report\\php' => '/phpunit/php-code-coverage/src/Report/PHP.php',
'sebastianbergmann\\codecoverage\\report\\text' => '/phpunit/php-code-coverage/src/Report/Text.php',
'sebastianbergmann\\codecoverage\\report\\xml\\buildinformation' => '/phpunit/php-code-coverage/src/Report/Xml/BuildInformation.php',
'sebastianbergmann\\codecoverage\\report\\xml\\coverage' => '/phpunit/php-code-coverage/src/Report/Xml/Coverage.php',
'sebastianbergmann\\codecoverage\\report\\xml\\directory' => '/phpunit/php-code-coverage/src/Report/Xml/Directory.php',
'sebastianbergmann\\codecoverage\\report\\xml\\facade' => '/phpunit/php-code-coverage/src/Report/Xml/Facade.php',
'sebastianbergmann\\codecoverage\\report\\xml\\file' => '/phpunit/php-code-coverage/src/Report/Xml/File.php',
'sebastianbergmann\\codecoverage\\report\\xml\\method' => '/phpunit/php-code-coverage/src/Report/Xml/Method.php',
'sebastianbergmann\\codecoverage\\report\\xml\\node' => '/phpunit/php-code-coverage/src/Report/Xml/Node.php',
'sebastianbergmann\\codecoverage\\report\\xml\\project' => '/phpunit/php-code-coverage/src/Report/Xml/Project.php',
'sebastianbergmann\\codecoverage\\report\\xml\\report' => '/phpunit/php-code-coverage/src/Report/Xml/Report.php',
'sebastianbergmann\\codecoverage\\report\\xml\\source' => '/phpunit/php-code-coverage/src/Report/Xml/Source.php',
'sebastianbergmann\\codecoverage\\report\\xml\\tests' => '/phpunit/php-code-coverage/src/Report/Xml/Tests.php',
'sebastianbergmann\\codecoverage\\report\\xml\\totals' => '/phpunit/php-code-coverage/src/Report/Xml/Totals.php',
'sebastianbergmann\\codecoverage\\report\\xml\\unit' => '/phpunit/php-code-coverage/src/Report/Xml/Unit.php',
'sebastianbergmann\\codecoverage\\runtimeexception' => '/phpunit/php-code-coverage/src/Exception/RuntimeException.php',
'sebastianbergmann\\codecoverage\\unintentionallycoveredcodeexception' => '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php',
'sebastianbergmann\\codecoverage\\util' => '/phpunit/php-code-coverage/src/Util.php',
'sebastianbergmann\\codecoverage\\version' => '/phpunit/php-code-coverage/src/Version.php',
'sebastianbergmann\\codeunitreverselookup\\wizard' => '/sebastian/code-unit-reverse-lookup/src/Wizard.php',
'sebastianbergmann\\comparator\\arraycomparator' => '/sebastian/comparator/src/ArrayComparator.php',
'sebastianbergmann\\comparator\\comparator' => '/sebastian/comparator/src/Comparator.php',
'sebastianbergmann\\comparator\\comparisonfailure' => '/sebastian/comparator/src/ComparisonFailure.php',
'sebastianbergmann\\comparator\\datetimecomparator' => '/sebastian/comparator/src/DateTimeComparator.php',
'sebastianbergmann\\comparator\\domnodecomparator' => '/sebastian/comparator/src/DOMNodeComparator.php',
'sebastianbergmann\\comparator\\doublecomparator' => '/sebastian/comparator/src/DoubleComparator.php',
'sebastianbergmann\\comparator\\exceptioncomparator' => '/sebastian/comparator/src/ExceptionComparator.php',
'sebastianbergmann\\comparator\\factory' => '/sebastian/comparator/src/Factory.php',
'sebastianbergmann\\comparator\\mockobjectcomparator' => '/sebastian/comparator/src/MockObjectComparator.php',
'sebastianbergmann\\comparator\\numericcomparator' => '/sebastian/comparator/src/NumericComparator.php',
'sebastianbergmann\\comparator\\objectcomparator' => '/sebastian/comparator/src/ObjectComparator.php',
'sebastianbergmann\\comparator\\resourcecomparator' => '/sebastian/comparator/src/ResourceComparator.php',
'sebastianbergmann\\comparator\\scalarcomparator' => '/sebastian/comparator/src/ScalarComparator.php',
'sebastianbergmann\\comparator\\splobjectstoragecomparator' => '/sebastian/comparator/src/SplObjectStorageComparator.php',
'sebastianbergmann\\comparator\\typecomparator' => '/sebastian/comparator/src/TypeComparator.php',
'sebastianbergmann\\diff\\chunk' => '/sebastian/diff/src/Chunk.php',
'sebastianbergmann\\diff\\diff' => '/sebastian/diff/src/Diff.php',
'sebastianbergmann\\diff\\differ' => '/sebastian/diff/src/Differ.php',
'sebastianbergmann\\diff\\line' => '/sebastian/diff/src/Line.php',
'sebastianbergmann\\diff\\longestcommonsubsequencecalculator' => '/sebastian/diff/src/LongestCommonSubsequenceCalculator.php',
'sebastianbergmann\\diff\\memoryefficientlongestcommonsubsequencecalculator' => '/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php',
'sebastianbergmann\\diff\\parser' => '/sebastian/diff/src/Parser.php',
'sebastianbergmann\\diff\\timeefficientlongestcommonsubsequencecalculator' => '/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php',
'sebastianbergmann\\environment\\console' => '/sebastian/environment/src/Console.php',
'sebastianbergmann\\environment\\operatingsystem' => '/sebastian/environment/src/OperatingSystem.php',
'sebastianbergmann\\environment\\runtime' => '/sebastian/environment/src/Runtime.php',
'sebastianbergmann\\exporter\\exporter' => '/sebastian/exporter/src/Exporter.php',
'sebastianbergmann\\fileiterator\\facade' => '/phpunit/php-file-iterator/src/Facade.php',
'sebastianbergmann\\fileiterator\\factory' => '/phpunit/php-file-iterator/src/Factory.php',
'sebastianbergmann\\fileiterator\\iterator' => '/phpunit/php-file-iterator/src/Iterator.php',
'sebastianbergmann\\finderfacade\\configuration' => '/sebastian/finder-facade/src/Configuration.php',
'sebastianbergmann\\finderfacade\\finderfacade' => '/sebastian/finder-facade/src/FinderFacade.php',
'sebastianbergmann\\globalstate\\blacklist' => '/sebastian/global-state/src/Blacklist.php',
'sebastianbergmann\\globalstate\\codeexporter' => '/sebastian/global-state/src/CodeExporter.php',
'sebastianbergmann\\globalstate\\restorer' => '/sebastian/global-state/src/Restorer.php',
'sebastianbergmann\\globalstate\\snapshot' => '/sebastian/global-state/src/Snapshot.php',
'sebastianbergmann\\objectenumerator\\enumerator' => '/sebastian/object-enumerator/src/Enumerator.php',
'sebastianbergmann\\objectenumerator\\exception' => '/sebastian/object-enumerator/src/Exception.php',
'sebastianbergmann\\objectenumerator\\invalidargumentexception' => '/sebastian/object-enumerator/src/InvalidArgumentException.php',
'sebastianbergmann\\objectreflector\\exception' => '/sebastian/object-reflector/src/Exception.php',
'sebastianbergmann\\objectreflector\\invalidargumentexception' => '/sebastian/object-reflector/src/InvalidArgumentException.php',
'sebastianbergmann\\objectreflector\\objectreflector' => '/sebastian/object-reflector/src/ObjectReflector.php',
'sebastianbergmann\\phpcov\\application' => '/src/Application.php',
'sebastianbergmann\\phpcov\\basecommand' => '/src/BaseCommand.php',
'sebastianbergmann\\phpcov\\executecommand' => '/src/ExecuteCommand.php',
'sebastianbergmann\\phpcov\\mergecommand' => '/src/MergeCommand.php',
'sebastianbergmann\\phpcov\\patchcoverage' => '/src/PatchCoverage.php',
'sebastianbergmann\\phpcov\\patchcoveragecommand' => '/src/PatchCoverageCommand.php',
'sebastianbergmann\\recursioncontext\\context' => '/sebastian/recursion-context/src/Context.php',
'sebastianbergmann\\recursioncontext\\exception' => '/sebastian/recursion-context/src/Exception.php',
'sebastianbergmann\\recursioncontext\\invalidargumentexception' => '/sebastian/recursion-context/src/InvalidArgumentException.php',
'sebastianbergmann\\resourceoperations\\resourceoperations' => '/sebastian/resource-operations/src/ResourceOperations.php',
'sebastianbergmann\\timer\\exception' => '/phpunit/php-timer/src/Exception.php',
'sebastianbergmann\\timer\\runtimeexception' => '/phpunit/php-timer/src/RuntimeException.php',
'sebastianbergmann\\timer\\timer' => '/phpunit/php-timer/src/Timer.php',
'sebastianbergmann\\type\\callabletype' => '/sebastian/type/src/CallableType.php',
'sebastianbergmann\\type\\genericobjecttype' => '/sebastian/type/src/GenericObjectType.php',
'sebastianbergmann\\type\\iterabletype' => '/sebastian/type/src/IterableType.php',
'sebastianbergmann\\type\\nulltype' => '/sebastian/type/src/NullType.php',
'sebastianbergmann\\type\\objecttype' => '/sebastian/type/src/ObjectType.php',
'sebastianbergmann\\type\\simpletype' => '/sebastian/type/src/SimpleType.php',
'sebastianbergmann\\type\\type' => '/sebastian/type/src/Type.php',
'sebastianbergmann\\type\\typename' => '/sebastian/type/src/TypeName.php',
'sebastianbergmann\\type\\unknowntype' => '/sebastian/type/src/UnknownType.php',
'sebastianbergmann\\type\\voidtype' => '/sebastian/type/src/VoidType.php',
'sebastianbergmann\\version' => '/sebastian/version/src/Version.php',
'symfony\\component\\console\\application' => '/symfony/console/Application.php',
'symfony\\component\\console\\command\\command' => '/symfony/console/Command/Command.php',
'symfony\\component\\console\\command\\helpcommand' => '/symfony/console/Command/HelpCommand.php',
'symfony\\component\\console\\command\\listcommand' => '/symfony/console/Command/ListCommand.php',
'symfony\\component\\console\\command\\lockabletrait' => '/symfony/console/Command/LockableTrait.php',
'symfony\\component\\console\\commandloader\\commandloaderinterface' => '/symfony/console/CommandLoader/CommandLoaderInterface.php',
'symfony\\component\\console\\commandloader\\containercommandloader' => '/symfony/console/CommandLoader/ContainerCommandLoader.php',
'symfony\\component\\console\\commandloader\\factorycommandloader' => '/symfony/console/CommandLoader/FactoryCommandLoader.php',
'symfony\\component\\console\\consoleevents' => '/symfony/console/ConsoleEvents.php',
'symfony\\component\\console\\dependencyinjection\\addconsolecommandpass' => '/symfony/console/DependencyInjection/AddConsoleCommandPass.php',
'symfony\\component\\console\\descriptor\\applicationdescription' => '/symfony/console/Descriptor/ApplicationDescription.php',
'symfony\\component\\console\\descriptor\\descriptor' => '/symfony/console/Descriptor/Descriptor.php',
'symfony\\component\\console\\descriptor\\descriptorinterface' => '/symfony/console/Descriptor/DescriptorInterface.php',
'symfony\\component\\console\\descriptor\\jsondescriptor' => '/symfony/console/Descriptor/JsonDescriptor.php',
'symfony\\component\\console\\descriptor\\markdowndescriptor' => '/symfony/console/Descriptor/MarkdownDescriptor.php',
'symfony\\component\\console\\descriptor\\textdescriptor' => '/symfony/console/Descriptor/TextDescriptor.php',
'symfony\\component\\console\\descriptor\\xmldescriptor' => '/symfony/console/Descriptor/XmlDescriptor.php',
'symfony\\component\\console\\event\\consolecommandevent' => '/symfony/console/Event/ConsoleCommandEvent.php',
'symfony\\component\\console\\event\\consoleerrorevent' => '/symfony/console/Event/ConsoleErrorEvent.php',
'symfony\\component\\console\\event\\consoleevent' => '/symfony/console/Event/ConsoleEvent.php',
'symfony\\component\\console\\event\\consoleterminateevent' => '/symfony/console/Event/ConsoleTerminateEvent.php',
'symfony\\component\\console\\eventlistener\\errorlistener' => '/symfony/console/EventListener/ErrorListener.php',
'symfony\\component\\console\\exception\\commandnotfoundexception' => '/symfony/console/Exception/CommandNotFoundException.php',
'symfony\\component\\console\\exception\\exceptioninterface' => '/symfony/console/Exception/ExceptionInterface.php',
'symfony\\component\\console\\exception\\invalidargumentexception' => '/symfony/console/Exception/InvalidArgumentException.php',
'symfony\\component\\console\\exception\\invalidoptionexception' => '/symfony/console/Exception/InvalidOptionException.php',
'symfony\\component\\console\\exception\\logicexception' => '/symfony/console/Exception/LogicException.php',
'symfony\\component\\console\\exception\\namespacenotfoundexception' => '/symfony/console/Exception/NamespaceNotFoundException.php',
'symfony\\component\\console\\exception\\runtimeexception' => '/symfony/console/Exception/RuntimeException.php',
'symfony\\component\\console\\formatter\\outputformatter' => '/symfony/console/Formatter/OutputFormatter.php',
'symfony\\component\\console\\formatter\\outputformatterinterface' => '/symfony/console/Formatter/OutputFormatterInterface.php',
'symfony\\component\\console\\formatter\\outputformatterstyle' => '/symfony/console/Formatter/OutputFormatterStyle.php',
'symfony\\component\\console\\formatter\\outputformatterstyleinterface' => '/symfony/console/Formatter/OutputFormatterStyleInterface.php',
'symfony\\component\\console\\formatter\\outputformatterstylestack' => '/symfony/console/Formatter/OutputFormatterStyleStack.php',
'symfony\\component\\console\\formatter\\wrappableoutputformatterinterface' => '/symfony/console/Formatter/WrappableOutputFormatterInterface.php',
'symfony\\component\\console\\helper\\debugformatterhelper' => '/symfony/console/Helper/DebugFormatterHelper.php',
'symfony\\component\\console\\helper\\descriptorhelper' => '/symfony/console/Helper/DescriptorHelper.php',
'symfony\\component\\console\\helper\\dumper' => '/symfony/console/Helper/Dumper.php',
'symfony\\component\\console\\helper\\formatterhelper' => '/symfony/console/Helper/FormatterHelper.php',
'symfony\\component\\console\\helper\\helper' => '/symfony/console/Helper/Helper.php',
'symfony\\component\\console\\helper\\helperinterface' => '/symfony/console/Helper/HelperInterface.php',
'symfony\\component\\console\\helper\\helperset' => '/symfony/console/Helper/HelperSet.php',
'symfony\\component\\console\\helper\\inputawarehelper' => '/symfony/console/Helper/InputAwareHelper.php',
'symfony\\component\\console\\helper\\processhelper' => '/symfony/console/Helper/ProcessHelper.php',
'symfony\\component\\console\\helper\\progressbar' => '/symfony/console/Helper/ProgressBar.php',
'symfony\\component\\console\\helper\\progressindicator' => '/symfony/console/Helper/ProgressIndicator.php',
'symfony\\component\\console\\helper\\questionhelper' => '/symfony/console/Helper/QuestionHelper.php',
'symfony\\component\\console\\helper\\symfonyquestionhelper' => '/symfony/console/Helper/SymfonyQuestionHelper.php',
'symfony\\component\\console\\helper\\table' => '/symfony/console/Helper/Table.php',
'symfony\\component\\console\\helper\\tablecell' => '/symfony/console/Helper/TableCell.php',
'symfony\\component\\console\\helper\\tablerows' => '/symfony/console/Helper/TableRows.php',
'symfony\\component\\console\\helper\\tableseparator' => '/symfony/console/Helper/TableSeparator.php',
'symfony\\component\\console\\helper\\tablestyle' => '/symfony/console/Helper/TableStyle.php',
'symfony\\component\\console\\input\\argvinput' => '/symfony/console/Input/ArgvInput.php',
'symfony\\component\\console\\input\\arrayinput' => '/symfony/console/Input/ArrayInput.php',
'symfony\\component\\console\\input\\input' => '/symfony/console/Input/Input.php',
'symfony\\component\\console\\input\\inputargument' => '/symfony/console/Input/InputArgument.php',
'symfony\\component\\console\\input\\inputawareinterface' => '/symfony/console/Input/InputAwareInterface.php',
'symfony\\component\\console\\input\\inputdefinition' => '/symfony/console/Input/InputDefinition.php',
'symfony\\component\\console\\input\\inputinterface' => '/symfony/console/Input/InputInterface.php',
'symfony\\component\\console\\input\\inputoption' => '/symfony/console/Input/InputOption.php',
'symfony\\component\\console\\input\\streamableinputinterface' => '/symfony/console/Input/StreamableInputInterface.php',
'symfony\\component\\console\\input\\stringinput' => '/symfony/console/Input/StringInput.php',
'symfony\\component\\console\\logger\\consolelogger' => '/symfony/console/Logger/ConsoleLogger.php',
'symfony\\component\\console\\output\\bufferedoutput' => '/symfony/console/Output/BufferedOutput.php',
'symfony\\component\\console\\output\\consoleoutput' => '/symfony/console/Output/ConsoleOutput.php',
'symfony\\component\\console\\output\\consoleoutputinterface' => '/symfony/console/Output/ConsoleOutputInterface.php',
'symfony\\component\\console\\output\\consolesectionoutput' => '/symfony/console/Output/ConsoleSectionOutput.php',
'symfony\\component\\console\\output\\nulloutput' => '/symfony/console/Output/NullOutput.php',
'symfony\\component\\console\\output\\output' => '/symfony/console/Output/Output.php',
'symfony\\component\\console\\output\\outputinterface' => '/symfony/console/Output/OutputInterface.php',
'symfony\\component\\console\\output\\streamoutput' => '/symfony/console/Output/StreamOutput.php',
'symfony\\component\\console\\question\\choicequestion' => '/symfony/console/Question/ChoiceQuestion.php',
'symfony\\component\\console\\question\\confirmationquestion' => '/symfony/console/Question/ConfirmationQuestion.php',
'symfony\\component\\console\\question\\question' => '/symfony/console/Question/Question.php',
'symfony\\component\\console\\style\\outputstyle' => '/symfony/console/Style/OutputStyle.php',
'symfony\\component\\console\\style\\styleinterface' => '/symfony/console/Style/StyleInterface.php',
'symfony\\component\\console\\style\\symfonystyle' => '/symfony/console/Style/SymfonyStyle.php',
'symfony\\component\\console\\terminal' => '/symfony/console/Terminal.php',
'symfony\\component\\console\\tester\\applicationtester' => '/symfony/console/Tester/ApplicationTester.php',
'symfony\\component\\console\\tester\\commandtester' => '/symfony/console/Tester/CommandTester.php',
'symfony\\component\\console\\tester\\testertrait' => '/symfony/console/Tester/TesterTrait.php',
'symfony\\component\\finder\\comparator\\comparator' => '/symfony/finder/Comparator/Comparator.php',
'symfony\\component\\finder\\comparator\\datecomparator' => '/symfony/finder/Comparator/DateComparator.php',
'symfony\\component\\finder\\comparator\\numbercomparator' => '/symfony/finder/Comparator/NumberComparator.php',
'symfony\\component\\finder\\exception\\accessdeniedexception' => '/symfony/finder/Exception/AccessDeniedException.php',
'symfony\\component\\finder\\exception\\directorynotfoundexception' => '/symfony/finder/Exception/DirectoryNotFoundException.php',
'symfony\\component\\finder\\finder' => '/symfony/finder/Finder.php',
'symfony\\component\\finder\\gitignore' => '/symfony/finder/Gitignore.php',
'symfony\\component\\finder\\glob' => '/symfony/finder/Glob.php',
'symfony\\component\\finder\\iterator\\customfilteriterator' => '/symfony/finder/Iterator/CustomFilterIterator.php',
'symfony\\component\\finder\\iterator\\daterangefilteriterator' => '/symfony/finder/Iterator/DateRangeFilterIterator.php',
'symfony\\component\\finder\\iterator\\depthrangefilteriterator' => '/symfony/finder/Iterator/DepthRangeFilterIterator.php',
'symfony\\component\\finder\\iterator\\excludedirectoryfilteriterator' => '/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php',
'symfony\\component\\finder\\iterator\\filecontentfilteriterator' => '/symfony/finder/Iterator/FilecontentFilterIterator.php',
'symfony\\component\\finder\\iterator\\filenamefilteriterator' => '/symfony/finder/Iterator/FilenameFilterIterator.php',
'symfony\\component\\finder\\iterator\\filetypefilteriterator' => '/symfony/finder/Iterator/FileTypeFilterIterator.php',
'symfony\\component\\finder\\iterator\\multiplepcrefilteriterator' => '/symfony/finder/Iterator/MultiplePcreFilterIterator.php',
'symfony\\component\\finder\\iterator\\pathfilteriterator' => '/symfony/finder/Iterator/PathFilterIterator.php',
'symfony\\component\\finder\\iterator\\recursivedirectoryiterator' => '/symfony/finder/Iterator/RecursiveDirectoryIterator.php',
'symfony\\component\\finder\\iterator\\sizerangefilteriterator' => '/symfony/finder/Iterator/SizeRangeFilterIterator.php',
'symfony\\component\\finder\\iterator\\sortableiterator' => '/symfony/finder/Iterator/SortableIterator.php',
'symfony\\component\\finder\\splfileinfo' => '/symfony/finder/SplFileInfo.php',
'symfony\\contracts\\service\\resetinterface' => '/symfony/service-contracts/ResetInterface.php',
'symfony\\contracts\\service\\servicelocatortrait' => '/symfony/service-contracts/ServiceLocatorTrait.php',
'symfony\\contracts\\service\\serviceproviderinterface' => '/symfony/service-contracts/ServiceProviderInterface.php',
'symfony\\contracts\\service\\servicesubscriberinterface' => '/symfony/service-contracts/ServiceSubscriberInterface.php',
'symfony\\contracts\\service\\servicesubscribertrait' => '/symfony/service-contracts/ServiceSubscriberTrait.php',
'symfony\\contracts\\service\\test\\servicelocatortest' => '/symfony/service-contracts/Test/ServiceLocatorTest.php',
'symfony\\polyfill\\ctype\\ctype' => '/symfony/polyfill-ctype/Ctype.php',
'symfony\\polyfill\\mbstring\\mbstring' => '/symfony/polyfill-mbstring/Mbstring.php',
'symfony\\polyfill\\php73\\php73' => '/symfony/polyfill-php73/Php73.php',
'text_template' => '/phpunit/php-text-template/src/Template.php',
'theseer\\fdom\\css\\dollarequalrule' => '/theseer-fdomdocument/css/DollarEqualRule.php',
'theseer\\fdom\\css\\notrule' => '/theseer-fdomdocument/css/NotRule.php',
'theseer\\fdom\\css\\nthchildrule' => '/theseer-fdomdocument/css/NthChildRule.php',
'theseer\\fdom\\css\\regexrule' => '/theseer-fdomdocument/css/RegexRule.php',
'theseer\\fdom\\css\\ruleinterface' => '/theseer-fdomdocument/css/RuleInterface.php',
'theseer\\fdom\\css\\translator' => '/theseer-fdomdocument/css/Translator.php',
'theseer\\fdom\\fdomdocument' => '/theseer-fdomdocument/fDOMDocument.php',
'theseer\\fdom\\fdomdocumentfragment' => '/theseer-fdomdocument/fDOMDocumentFragment.php',
'theseer\\fdom\\fdomelement' => '/theseer-fdomdocument/fDOMElement.php',
'theseer\\fdom\\fdomexception' => '/theseer-fdomdocument/fDOMException.php',
'theseer\\fdom\\fdomnode' => '/theseer-fdomdocument/fDOMNode.php',
'theseer\\fdom\\fdomxpath' => '/theseer-fdomdocument/fDOMXPath.php',
'theseer\\fdom\\xpathquery' => '/theseer-fdomdocument/XPathQuery.php',
'theseer\\fdom\\xpathqueryexception' => '/theseer-fdomdocument/XPathQueryException.php',
'theseer\\tokenizer\\exception' => '/theseer-tokenizer/Exception.php',
'theseer\\tokenizer\\namespaceuri' => '/theseer-tokenizer/NamespaceUri.php',
'theseer\\tokenizer\\namespaceuriexception' => '/theseer-tokenizer/NamespaceUriException.php',
'theseer\\tokenizer\\token' => '/theseer-tokenizer/Token.php',
'theseer\\tokenizer\\tokencollection' => '/theseer-tokenizer/TokenCollection.php',
'theseer\\tokenizer\\tokencollectionexception' => '/theseer-tokenizer/TokenCollectionException.php',
'theseer\\tokenizer\\tokenizer' => '/theseer-tokenizer/Tokenizer.php',
'theseer\\tokenizer\\xmlserializer' => '/theseer-tokenizer/XMLSerializer.php'
);
}
$class = strtolower($class);
if (isset($classes[$class])) {
require 'phar://phpcov-6.0.1.phar' . $classes[$class];
}
}
);
Phar::mapPhar('phpcov-6.0.1.phar');
if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '--manifest') {
print file_get_contents(__PHPCOV_PHAR_ROOT__ . '/manifest.txt');
exit;
}
$application = new SebastianBergmann\PHPCOV\Application;
$application->run();
__HALT_COMPILER(); ?>
<8A><95><00>phpcov-6.0.1.pharsrc/BaseCommand.php<68>"<22><>]<5D><00><>.<2E><>src/PatchCoverage.php<68> "<22><>]<5D> <>c<9D><63>src/Application.phpi"<22><>]i<1F>W{<7B>src/PatchCoverageCommand.php<68> "<22><>]<5D> &37'<27>src/ExecuteCommand.php"<22><>]#|<7C>src/MergeCommand.php<68> "<22><>]<5D> +UyT<79> manifest.txt["<22><>][|1<<3C>%theseer-tokenizer/TokenCollection.phpr
"<22><>]r
<00>g9<67>+theseer-tokenizer/NamespaceUriException.phpr"<22><>]r+<2B><>A<F0>theseer-tokenizer/Exception.phpg"<22><>]gլ<>X<D7>"theseer-tokenizer/NamespaceUri.phpw"<22><>]w 'U<><55>#theseer-tokenizer/XMLSerializer.php<68> "<22><>]<5D> (l<>ٶ.theseer-tokenizer/TokenCollectionException.phpu"<22><>]u<00><>BK<42>theseer-tokenizer/Tokenizer.php3"<22><>]3<00>a<F2><61><F7>theseer-tokenizer/Token.phpq"<22><>]q<00><>?<3F><>*phpunit/php-text-template/src/Template.php<68> "<22><>]<5D> <00>w4<77><34>phpunit/php-timer/src/Timer.php<68> "<22><>]<5D> :<3A><>(<28>#phpunit/php-timer/src/Exception.phpC"<22><>]C/u<><75>*phpunit/php-timer/src/RuntimeException.php{"<22><>]{<00>%<25><><EF>)phpunit/php-file-iterator/src/Factory.php<68>"<22><>]<5D><00>l Ҷ*phpunit/php-file-iterator/src/Iterator.php "<22><>] <00>%<25>޶(phpunit/php-file-iterator/src/Facade.php<68> "<22><>]<5D> k*<2A>̶+phpunit/php-token-stream/src/Token/Util.php<68>"<22><>]<5D>T~Z˶<phpunit/php-token-stream/src/Token/Stream/CachingFactory.php<68>"<22><>]<5D> \h<><68>-phpunit/php-token-stream/src/Token/Stream.php<68>?"<22><>]<5D>?<02>Qi<51>&phpunit/php-token-stream/src/Token.php<68>a"<22><>]<5D>aP<<3C><05>.phpunit/php-code-coverage/src/CodeCoverage.php<68>r"<22><>]<5D>r8i<15>)phpunit/php-code-coverage/src/Version.php<68>"<22><>]<5D>O<>hU<68>&phpunit/php-code-coverage/src/Util.phpf"<22><>]fQ9r<39>3phpunit/php-code-coverage/src/Node/AbstractNode.phpN"<22><>]NH <0B>ʶ/phpunit/php-code-coverage/src/Node/Iterator.php:"<22><>]:<00>_<97>c<D5>.phpunit/php-code-coverage/src/Node/Builder.php<68>"<22><>]<5D>a6<61>Ҷ0phpunit/php-code-coverage/src/Node/Directory.phpq$"<22><>]q$nr)ö+phpunit/php-code-coverage/src/Node/File.php6@"<22><>]6@<00>dö4phpunit/php-code-coverage/src/Report/Xml/Project.php= "<22><>]= \<5C>и<E4>3phpunit/php-code-coverage/src/Report/Xml/Source.php"<22><>]v<><76>]<5D>3phpunit/php-code-coverage/src/Report/Xml/Report.php<68> "<22><>]<5D> ۈ<>|<7C>1phpunit/php-code-coverage/src/Report/Xml/Unit.php<68>
"<22><>]<5D>
m<>1phpunit/php-code-coverage/src/Report/Xml/Node.phpf"<22><>]f<00>aD<61><44>6phpunit/php-code-coverage/src/Report/Xml/Directory.phpp"<22><>]p <20>5phpunit/php-code-coverage/src/Report/Xml/Coverage.php<68>"<22><>]<5D>*'mc<6D>3phpunit/php-code-coverage/src/Report/Xml/Method.php<68>"<22><>]<5D>3<>1<8D>2phpunit/php-code-coverage/src/Report/Xml/Tests.php<68>"<22><>]<5D>Q<>l<C7>3phpunit/php-code-coverage/src/Report/Xml/Facade.php<68> "<22><>]<5D> )"<22><><FF>3phpunit/php-code-coverage/src/Report/Xml/Totals.php0"<22><>]0Z<>h<E0><68>=phpunit/php-code-coverage/src/Report/Xml/BuildInformation.php<68> "<22><>]<5D> <00><><A1>X<9B>1phpunit/php-code-coverage/src/Report/Xml/File.php<68>"<22><>]<5D><00><>6phpunit/php-code-coverage/src/Report/Html/Renderer.php<68>!"<22><>]<5D>!<00>\x?<3F>@phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php<68> "<22><>]<5D> <00>c<C5>~<7E>@phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.phpp%"<22><>]p%<00><>)<29><>Jphpunit/php-code-coverage/src/Report/Html/Renderer/Template/file.html.dist
"<22><>]
<00>f "<22>Tphpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory_item.html.distA"<22><>]Ads<11>Qphpunit/php-code-coverage/src/Report/Html/Renderer/Template/method_item.html.dist<73>"<22><>]<5D><00><>s:<3A>Ophpunit/php-code-coverage/src/Report/Html/Renderer/Template/icons/file-code.svg0"<22><>]0<00>QUU<55>Tphpunit/php-code-coverage/src/Report/Html/Renderer/Template/icons/file-directory.svg<76>"<22><>]<5D><00><>Z<DA><5A>Ophpunit/php-code-coverage/src/Report/Html/Renderer/Template/dashboard.html.distG"<22><>]G<01><>l<C4>Rphpunit/php-code-coverage/src/Report/Html/Renderer/Template/coverage_bar.html.dist'"<22><>]'<00>O}<05>Ophpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_item.html.distt"<22><>]t<00><08><><BF>Lphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/octicons.cssX"<22><>]X'#<23><0F>Jphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/custom.css"<22><>]<00>Mphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/nv.d3.min.cssX%"<22><>]X%<00>0,<2C>Iphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/style.css<73>"<22><>]<5D><00><>v<08>Qphpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/bootstrap.min.cssn`"<22><>]n`<00><>ӶOphpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory.html.dist<73>"<22><>]<5D>G<>M<C9><4D>Ophpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/bootstrap.min.js<6A><73>"<22><>]<5D><>'<27>^<5E><>Lphpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/jquery.min.jsQX"<22><>]QXQ<><51>[<5B>Kphpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/nv.d3.min.js<6A>R"<22><>]<5D>R<Ms<>Hphpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/d3.min.js<6A>P"<22><>]<5D>P<00>h<C5>b<E9>Fphpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/file.js<6A>"<22><>]<5D>b<>Lphpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/popper.min.js<6A>R"<22><>]<5D>RZ<>\<01>;phpunit/php-code-coverage/src/Report/Html/Renderer/File.php<68>I"<22><>]<5D>I9<>j<95><6A>4phpunit/php-code-coverage/src/Report/Html/Facade.phpO"<22><>]O<03>lN<6C>,phpunit/php-code-coverage/src/Report/PHP.phpl"<22><>]l5^<5E>-<2D>/phpunit/php-code-coverage/src/Report/Clover.php<68>)"<22><>]<5D>)DQ<44><51><FC>/phpunit/php-code-coverage/src/Report/Crap4j.phpv"<22><>]v<00>.<2E>(<28>-phpunit/php-code-coverage/src/Report/Text.phpk""<22><>]k"<00>><3E>ܶOphpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.phpM"<22><>]M)<29><><89><8F>5phpunit/php-code-coverage/src/Exception/Exception.php<68>"<22><>]<5D><00><>~<7E>Dphpunit/php-code-coverage/src/Exception/InvalidArgumentException.php"<22><>]e<>Uu<55>Lphpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php<68>"<22><>]<5D>ʡ<><CAA1><A3>Kphpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php<68>"<22><>]<5D>X<> <0C><phpunit/php-code-coverage/src/Exception/RuntimeException.php<68>"<22><>]<5D><00>bPD<50>/phpunit/php-code-coverage/src/Driver/Xdebug.php<68>
"<22><>]<5D>
&" <20>-phpunit/php-code-coverage/src/Driver/PCOV.php<68>"<22><>]<5D>N<>=<3D>/phpunit/php-code-coverage/src/Driver/Driver.php"<22><>]N<><4E><1D>/phpunit/php-code-coverage/src/Driver/PHPDBG.php<68>
"<22><>]<5D>
d<><64><07>(phpunit/php-code-coverage/src/Filter.phpE"<22><>]ER}<7D><>)phpunit/phpunit/src/TextUI/TestRunner.php<68><70>"<22><>]<5D><><00>e8<65><38>#phpunit/phpunit/src/TextUI/Help.php<68>0"<22><>]<5D>0<00><>U<B8><55>&phpunit/phpunit/src/TextUI/Command.php]<5D>"<22><>]]<5D>jf@<40>(phpunit/phpunit/src/TextUI/Exception.php<68>"<22><>]<5D><00><><88>۶,phpunit/phpunit/src/TextUI/ResultPrinter.php3;"<22><>]3;ϐ<> <09>!phpunit/phpunit/src/Exception.php<68>"<22><>]<5D><17><>Q<A0>.phpunit/phpunit/src/Util/RegularExpression.phpD"<22><>]D<00>np[<5B>!phpunit/phpunit/src/Util/Test.phpv"<22><>]v<00><><B9><18>(phpunit/phpunit/src/Util/GlobalState.phpJ"<22><>]J<00>`:U<>6phpunit/phpunit/src/Util/TestDox/TextResultPrinter.php$"<22><>]$zi><3E><>3phpunit/phpunit/src/Util/TestDox/TestDoxPrinter.php<68>)"<22><>]<5D>)H<>
Ŷ6phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php<68>
"<22><>]<5D>
<00><>w<AB><77>6phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.phpD*"<22><>]D*7<><37>|<7C>3phpunit/phpunit/src/Util/TestDox/NamePrettifier.php<68> "<22><>]<5D> <58>5phpunit/phpunit/src/Util/TestDox/XmlResultPrinter.php<68>"<22><>]<5D><00><>2phpunit/phpunit/src/Util/TestDox/ResultPrinter.php<68>"<22><>]<5D><00><>Hw<48>'phpunit/phpunit/src/Util/Filesystem.php<68>"<22><>]<5D>~<7E>F<C1><46>$phpunit/phpunit/src/Util/Printer.php<68> "<22><>]<5D> <00><><88>̶'phpunit/phpunit/src/Util/FileLoader.php- "<22><>]- <00><>z<7F>0phpunit/phpunit/src/Util/Annotation/DocBlock.phphK"<22><>]hK-j;<3B>0phpunit/phpunit/src/Util/Annotation/Registry.phpt "<22><>]t [|<7C>2<F2>"phpunit/phpunit/src/Util/Color.php<68>"<22><>]<5D><00><><9C><C8><C1> phpunit/phpunit/src/Util/Xml.php!"<22><>]!x<1D>Ӷ8phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php<68>"<22><>]<5D>»<>P<AA>&phpunit/phpunit/src/Util/Log/JUnit.php<68>,"<22><>]<5D>,D<><44>(<28>)phpunit/phpunit/src/Util/Log/TeamCity.php<68>("<22><>]<5D>(<00>8*ڶ!phpunit/phpunit/src/Util/Json.php"
"<22><>]"
<00>. y<>&phpunit/phpunit/src/Util/Exception.php<68>"<22><>]<5D>#!><3E>3phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php<68>("<22><>]<5D>(DA<><41>2phpunit/phpunit/src/Util/PHP/WindowsPhpProcess.phpt"<22><>]t<00><>2K<32>+phpunit/phpunit/src/Util/PHP/eval-stdin.php-"<22><>]-<00>-rX<72>2phpunit/phpunit/src/Util/PHP/DefaultPhpProcess.php<68>"<22><>]<5D><00>f<FE>*phpunit/phpunit/src/Util/Configuration.php6<70>"<22><>]6<>p<>X<CA><58>3phpunit/phpunit/src/Util/ConfigurationGenerator.phpe"<22><>]e<00><><80><00>!phpunit/phpunit/src/Util/Type.php<68>"<22><>]<5D>z<>#M<>#phpunit/phpunit/src/Util/Getopt.php<68>"<22><>]<5D><00><>g<FA><67>)phpunit/phpunit/src/Util/ErrorHandler.php<68>"<22><>]<5D>8Y#<1A>0phpunit/phpunit/src/Util/XmlTestListRenderer.php%
"<22><>]%
`<60><>B<AF>1phpunit/phpunit/src/Util/TextTestListRenderer.php@"<22><>]@7<><10>&phpunit/phpunit/src/Util/Blacklist.php"<22><>]F%<25><>#phpunit/phpunit/src/Util/Filter.phph
"<22><>]h
<00>O <20>&phpunit/phpunit/src/Runner/Version.php<68>"<22><>]<5D>I~<7E>9<83>-phpunit/phpunit/src/Runner/BaseTestRunner.phpP"<22><>]P<00>!O<>5phpunit/phpunit/src/Runner/DefaultTestResultCache.php<68>"<22><>]<5D>?]<5D>׶-phpunit/phpunit/src/Runner/Filter/Factory.phpQ"<22><>]QW<><08><>@phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.phpB"<22><>]B<00><>ċ<8F>8phpunit/phpunit/src/Runner/Filter/NameFilterIterator.phpl "<22><>]l <00>;
h<B6>9phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php<68>"<22><>]<5D><00>/<2F><04>@phpunit/phpunit/src/Runner/Filter/IncludeGroupFilterIterator.phpA"<22><>]A<00><>a<07>(phpunit/phpunit/src/Runner/Exception.php<68>"<22><>]<5D>m<><6D>]<5D>3phpunit/phpunit/src/Runner/ResultCacheExtension.php* "<22><>]* <00><18><><80>.phpunit/phpunit/src/Runner/TestResultCache.php<68>"<22><>]<5D><00>Y)ɶ.phpunit/phpunit/src/Runner/TestSuiteSorter.phpq3"<22><>]q3g5<67><35><9B>8phpunit/phpunit/src/Runner/Hook/AfterTestWarningHook.php<68>"<22><>]<5D><00><>ֆ<AB>8phpunit/phpunit/src/Runner/Hook/AfterSkippedTestHook.php<68>"<22><>]<5D><00>J <0C>(phpunit/phpunit/src/Runner/Hook/Hook.php+"<22><>]+kXM<58><4D>,phpunit/phpunit/src/Runner/Hook/TestHook.php<"<22><>]<xᤸ<78>2phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php<68>"<22><>]<5D>z<>ݶ6phpunit/phpunit/src/Runner/Hook/AfterTestErrorHook.php<68>"<22><>]<5D>LX<4C>Ƕ7phpunit/phpunit/src/Runner/Hook/TestListenerAdapter.php"<22><>]>P<><50><9F>7phpunit/phpunit/src/Runner/Hook/BeforeFirstTestHook.php{"<22><>]{<00>.<2E>6phpunit/phpunit/src/Runner/Hook/AfterRiskyTestHook.php<68>"<22><>]<5D>jY<6A>M<DE>5phpunit/phpunit/src/Runner/Hook/AfterLastTestHook.phpw"<22><>]w<13>~F<>1phpunit/phpunit/src/Runner/Hook/AfterTestHook.phpU"<22><>]UfTx<05>8phpunit/phpunit/src/Runner/Hook/AfterTestFailureHook.php<68>"<22><>]<5D>z<>c<83><63>;phpunit/phpunit/src/Runner/Hook/AfterSuccessfulTestHook.php<68>"<22><>]<5D>j3<6A><33><B1>;phpunit/phpunit/src/Runner/Hook/AfterIncompleteTestHook.php<68>"<22><>]<5D><00>*K<>2phpunit/phpunit/src/Runner/NullTestResultCache.phpv"<22><>]vr<02><13>.phpunit/phpunit/src/Runner/TestSuiteLoader.php8"<22><>]8+<2B>6phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php<68>"<22><>]<5D>T*8<><38>+phpunit/phpunit/src/Runner/PhptTestCase.php=Q"<22><>]=Qο<>W<9A>+phpunit/phpunit/src/Framework/TestSuite.php<68>S"<22><>]<5D>S<00><>d"<22>&phpunit/phpunit/src/Framework/Test.php"<22><>]<00>8<>,phpunit/phpunit/src/Framework/TestResult.php<68>{"<22><>]<5D>{<00><>&<26>1phpunit/phpunit/src/Framework/SkippedTestCase.php<68>"<22><>]<5D><00><><E8><16>-phpunit/phpunit/src/Framework/TestBuilder.php4"<22><>]4<00>T<EF>l<FB>(phpunit/phpunit/src/Framework/Assert.php[<5B>"<22><>][<5B>k<><6B>Z<FD>4phpunit/phpunit/src/Framework/IncompleteTestCase.php<68>"<22><>]<5D>)<29>HM<48>7phpunit/phpunit/src/Framework/DataProviderTestSuite.php<68>"<22><>]<5D>sə$<24>.phpunit/phpunit/src/Framework/TestListener.phpm"<22><>]m41Nq<4E>Aphpunit/phpunit/src/Framework/Exception/SkippedTestSuiteError.php<68>"<22><>]<5D>f<><66><13>@phpunit/phpunit/src/Framework/Exception/AssertionFailedError.phpj"<22><>]j<00>ZY<5A><59>:phpunit/phpunit/src/Framework/Exception/RiskyTestError.php<68>"<22><>]<5D>9<>d<8D><64>:phpunit/phpunit/src/Framework/Exception/SyntheticError.php&"<22><>]&i<>ٶAphpunit/phpunit/src/Framework/Exception/CodeCoverageException.php<68>"<22><>]<5D><߆]<5D>7phpunit/phpunit/src/Framework/Exception/OutputError.php<68>"<22><>]<5D><00><>:t<>5phpunit/phpunit/src/Framework/Exception/Exception.phpD "<22><>]D |h`P<><phpunit/phpunit/src/Framework/Exception/SkippedTestError.php<68>"<22><>]<5D>=<3D>@<40><>Dphpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php "<22><>] ><3E><>G<EC>Dphpunit/phpunit/src/Framework/Exception/InvalidArgumentException.php,"<22><>],<00><>ԶDphpunit/phpunit/src/Framework/Exception/UnexpectedValueException.phpV"<22><>]V<00><>{<7B><>Kphpunit/phpunit/src/Framework/Exception/UnintentionallyCoveredCodeError.php<68>"<22><>]<5D>i<>t<A3><74>?phpunit/phpunit/src/Framework/Exception/IncompleteTestError.php<68>"<22><>]<5D><00><>)R<>Lphpunit/phpunit/src/Framework/Exception/MissingCoversAnnotationException.php<68>"<22><>]<5D>1<>ȶAphpunit/phpunit/src/Framework/Exception/SyntheticSkippedError.php<68>"<22><>]<5D><00><>ӏ<DA>Hphpunit/phpunit/src/Framework/Exception/InvalidCoversTargetException.php<68>"<22><>]<5D><00>U<C4><55><8F>Kphpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php<68>"<22><>]<5D><00>p<C1><70><C7>Fphpunit/phpunit/src/Framework/Exception/ExpectationFailedException.php<68>"<22><>]<5D>ݘ<><DD98><FF>3phpunit/phpunit/src/Framework/Exception/Warning.phpc"<22><>]c <20><>J<B1>Hphpunit/phpunit/src/Framework/Exception/InvalidDataProviderException.php<68>"<22><>]<5D><00><><EB>R<DE>6phpunit/phpunit/src/Framework/MockObject/Generator.php%~"<22><>]%~I<><49>5phpunit/phpunit/src/Framework/MockObject/MockType.php<68>"<22><>]<5D><00>'<27>5<F0>8phpunit/phpunit/src/Framework/MockObject/MockBuilder.php+"<22><>]+wT<77><54><FF>7phpunit/phpunit/src/Framework/MockObject/MockObject.php
"<22><>]
<00>r<><72>7phpunit/phpunit/src/Framework/MockObject/Api/Method.php<68>"<22><>]<5D>8q<38>ͶDphpunit/phpunit/src/Framework/MockObject/Api/UnmockedCloneMethod.phpF"<22><>]F!O <0A>4phpunit/phpunit/src/Framework/MockObject/Api/Api.php<68> "<22><>]<5D> <00>N쟶Bphpunit/phpunit/src/Framework/MockObject/Api/MockedCloneMethod.php("<22><>](ψꎶ>phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php<68>"<22><>]<5D>زu%<25>Wphpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php<68>"<22><>]<5D>t<><74><BC>Mphpunit/phpunit/src/Framework/MockObject/Exception/BadMethodCallException.php<68>"<22><>]<5D><00><><87>i<8E>ephpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php"<22><>]<00>}-<2D><>@phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php<68>"<22><>]<5D><00><D0>Gphpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php<68>"<22><>]<5D>"bB<62><42>7phpunit/phpunit/src/Framework/MockObject/Verifiable.php<68>"<22><>]<5D><00><>/v<>4phpunit/phpunit/src/Framework/MockObject/Matcher.php#"<22><>]#<00>آ<D1><D8A2>Aphpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php<68>"<22><>]<5D>I<>h<AB><68>@phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php<68>"<22><>]<5D>Tw r<>Bphpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php<68>"<22><>]<5D>emDŶ;phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php<68>"<22><>]<5D>J<>F<10>6phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php2"<22><>]27<>c<D3><63>Aphpunit/phpunit/src/Framework/MockObject/Stub/ReturnReference.php<68>"<22><>]<5D><00><><E3>G<D9>@phpunit/phpunit/src/Framework/MockObject/Stub/ReturnCallback.php<68>"<22><>]<5D><00>
Ū<B6>@phpunit/phpunit/src/Framework/MockObject/Stub/ReturnArgument.php<68>"<22><>]<5D><00><><A2>
<B6><phpunit/phpunit/src/Framework/MockObject/Stub/ReturnSelf.php"<22><>]<00><> <09><phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php<68>"<22><>]<5D><00><>D<>1phpunit/phpunit/src/Framework/MockObject/Stub.php<68>"<22><>]<5D><00>Pl?<3F>6phpunit/phpunit/src/Framework/MockObject/MockClass.phpy"<22><>]y<00><><ED>6<D1>7phpunit/phpunit/src/Framework/MockObject/Invocation.php<68>"<22><>]<5D>#n9{<7B>?phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php<68>"<22><>]<5D><00> <0B><>Aphpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.phpD"<22><>]DM<><0F><>Dphpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastOnce.php&"<22><>]&<00>W<9E>Y<A8><phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.php<68>"<22><>]<5D><00>)3<><33>Ephpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastCount.php<68>"<22><>]<5D><0E><><06>@phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtIndex.php<68>"<22><>]<5D>J<><4A><1F>@phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php<68>"<22><>]<5D><00><>B<E2><42>?phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.php<68>"<22><>]<5D><00><>;<3B><>>phpunit/phpunit/src/Framework/MockObject/Rule/InvokedCount.php<
"<22><>]<
<00>]i<><phpunit/phpunit/src/Framework/MockObject/Rule/MethodName.php<68>"<22><>]<5D>R<>
ĶAphpunit/phpunit/src/Framework/MockObject/Rule/InvocationOrder.php<68>"<22><>]<5D><00><><95><D7><DD>Dphpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtMostCount.php<68>"<22><>]<5D><00><>l<>Gphpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php'"<22><>]'<><C386>=phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php<68>"<22><>]<5D><00><>}"<22>9phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php<68>"<22><>]<5D><00>?<3F> <0C>:phpunit/phpunit/src/Framework/MockObject/Builder/Match.php<68>"<22><>]<5D><00><><B7>E<E0>Dphpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php<68>"<22><>]<5D>+<2B>}<7D><>Ephpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php|"<22><>]|'<27><><BB><A7>Fphpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php<68>"<22><>]<5D><00><><EC><0F>Dphpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.phpC"<22><>]C}k<><6B><BF>6phpunit/phpunit/src/Framework/MockObject/MockTrait.php<68>"<22><>]<5D><00><><C9>l<B7>:phpunit/phpunit/src/Framework/MockObject/MockMethodSet.php<68>"<22><>]<5D><<3C>)<29><>7phpunit/phpunit/src/Framework/MockObject/MockMethod.php<68>,"<22><>]<5D>,<00><>R<91><52>0phpunit/phpunit/src/Framework/SelfDescribing.php"<22><>]<00><>F<8D><46>0phpunit/phpunit/src/Framework/IncompleteTest.php<68>"<22><>]<5D>#9d<39><64>*phpunit/phpunit/src/Framework/TestCase.php<68>)"<22><>]<5D>);<3B><00><>-phpunit/phpunit/src/Framework/SkippedTest.php<68>"<22><>]<5D><13>2phpunit/phpunit/src/Framework/ExceptionWrapper.php: "<22><>]: <00><>\F<>Cphpunit/phpunit/src/Framework/TestListenerDefaultImplementation.php<68>"<22><>]<5D>T]ꙶ7phpunit/phpunit/src/Framework/Constraint/LogicalXor.php@ "<22><>]@ <00>3n<33><6E>>phpunit/phpunit/src/Framework/Constraint/RegularExpression.php5"<22><>]5F<>gC<67>4phpunit/phpunit/src/Framework/Constraint/IsEqual.php"<22><>]z<><7A>ζLphpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php@"<22><>]@<00><><85> <0B>7phpunit/phpunit/src/Framework/Constraint/Constraint.php"<22><>]<00><><A5>o<C7>7phpunit/phpunit/src/Framework/Constraint/IsInfinite.php>"<22><>]><00><04><><E3>;phpunit/phpunit/src/Framework/Constraint/StringContains.php<68>"<22><>]<5D><00>N0T<30>6phpunit/phpunit/src/Framework/Constraint/LogicalOr.php<68>
"<22><>]<5D>
"0<>l<90>5phpunit/phpunit/src/Framework/Constraint/Callback.php"<22><>]<00><18><>8phpunit/phpunit/src/Framework/Constraint/JsonMatches.php: "<22><>]: \<5C>F<>3phpunit/phpunit/src/Framework/Constraint/IsTrue.php-"<22><>]-<00><32>7phpunit/phpunit/src/Framework/Constraint/LogicalNot.php<68>"<22><>]<5D>?<3F><>ʶ:phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php;"<22><>];<1E><><F6><D4>9phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php<68>"<22><>]<5D> ]<5D>8<8A>3phpunit/phpunit/src/Framework/Constraint/IsJson.php "<22><>] <00><><D0>g<EE>8phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php<68>"<22><>]<5D><00>~y<><79>;phpunit/phpunit/src/Framework/Constraint/StringEndsWith.phpO"<22><>]O<00>
<A7><B6>8phpunit/phpunit/src/Framework/Constraint/ArraySubset.php"<22><>]<00><>k<F6><6B>3phpunit/phpunit/src/Framework/Constraint/IsNull.php-"<22><>]-ɭw<C9AD><77>6phpunit/phpunit/src/Framework/Constraint/Exception.php<68>"<22><>]<5D>h_<68><5F><FE>8phpunit/phpunit/src/Framework/Constraint/GreaterThan.php<68>"<22><>]<5D><00><><E5>Ƕ5phpunit/phpunit/src/Framework/Constraint/LessThan.php<68>"<22><>]<5D><00>
4<B6>7phpunit/phpunit/src/Framework/Constraint/FileExists.php["<22><>][zEPe<50>5phpunit/phpunit/src/Framework/Constraint/IsFinite.php6"<22><>]6{0jR<6A>8phpunit/phpunit/src/Framework/Constraint/IsIdentical.php<68>"<22><>]<5D>㯳c<E3AFB3>4phpunit/phpunit/src/Framework/Constraint/IsEmpty.phpG"<22><>]G<00><>'Y<>Dphpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php<68>"<22><>]<5D>{( /<2F>=phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php6"<22><>]6<00> <09>N<94>Dphpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php<68>"<22><>]<5D>?<3F>~<7E><>7phpunit/phpunit/src/Framework/Constraint/IsWritable.phpd"<22><>]d<00><>_P<5F>6phpunit/phpunit/src/Framework/Constraint/Attribute.php<68>"<22><>]<5D><00><07><>?phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.phpb"<22><>]b<00><>t`<60><phpunit/phpunit/src/Framework/Constraint/DirectoryExists.phpj"<22><>]j<00><1F>޶Kphpunit/phpunit/src/Framework/Constraint/StringMatchesFormatDescription.phpe
"<22><>]e
nG<><47>6phpunit/phpunit/src/Framework/Constraint/Composite.phpN"<22><>]N<00><>7<86><37>4phpunit/phpunit/src/Framework/Constraint/IsFalse.php1"<22><>]1<00><1B><>7phpunit/phpunit/src/Framework/Constraint/LogicalAnd.phpY "<22><>]Y <00>2<A3>/<2F>Nphpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegularExpression.php]"<22><>]]<00> <1A>2phpunit/phpunit/src/Framework/Constraint/Count.php<68> "<22><>]<5D> <00><><88><BE><B3>3phpunit/phpunit/src/Framework/Constraint/IsType.php<68>"<22><>]<5D><00><><A8>K<9B>@phpunit/phpunit/src/Framework/Constraint/TraversableContains.php` "<22><>]` \ <0B>m<89>2phpunit/phpunit/src/Framework/Constraint/IsNan.php*"<22><>]*_h<5F>۶>phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.phpl"<22><>]l=<3D><>'<27>7phpunit/phpunit/src/Framework/Constraint/IsReadable.phpd"<22><>]d<00><10><><86>=phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php<68>"<22><>]<5D><13>5phpunit/phpunit/src/Framework/Constraint/SameSize.php<68>"<22><>]<5D><00>i*<2A><>7phpunit/phpunit/src/Framework/Constraint/IsAnything.php"<22><>]<00><1D>t<F8>-phpunit/phpunit/src/Framework/TestFailure.php<68> "<22><>]<5D> 9<><39>3phpunit/phpunit/src/Framework/TestSuiteIterator.php<68>"<22><>]<5D><00><>6<0E>2phpunit/phpunit/src/Framework/Assert/Functions.php2"<22><>]2‹"<22>-phpunit/phpunit/src/Framework/Error/Error.phpW"<22><>]W <20>2phpunit/phpunit/src/Framework/Error/Deprecated.phpJ"<22><>]J<12><><94><9D>.phpunit/phpunit/src/Framework/Error/Notice.phpF"<22><>]F<00>"o<><6F>/phpunit/phpunit/src/Framework/Error/Warning.phpG"<22><>]G<00><17>}<7D>1phpunit/phpunit/src/Framework/WarningTestCase.phpE"<22><>]E<10>$d<>@phpunit/phpunit/src/Framework/InvalidParameterGroupException.php<68>"<22><>]<5D>}<7D><>#<23>"phpunit/phpunit/.phpstorm.meta.php<68>"<22><>]<5D><00><>G<93><47>"theseer-fdomdocument/fDOMXPath.php<68>"<22><>]<5D><00>!X̶$theseer-fdomdocument/fDOMElement.php<68>8"<22><>]<5D>8<00><>o<AA><6F>&theseer-fdomdocument/fDOMException.php<68>"<22><>]<5D><00>d<96>O<CD>,theseer-fdomdocument/XPathQueryException.php<68>"<22><>]<5D><00><>*<2A><>#theseer-fdomdocument/XPathQuery.php<68>"<22><>]<5D><00><><CF>߶!theseer-fdomdocument/autoload.php<68>"<22><>]<5D>!<21>H<01>-theseer-fdomdocument/fDOMDocumentFragment.phpI"<22><>]I]<5D>~<7E>!theseer-fdomdocument/fDOMNode.php<68>"<22><>]<5D>VGQ<47>%theseer-fdomdocument/fDOMDocument.php"["<22><>]"[7h&<26>$theseer-fdomdocument/css/NotRule.php7"<22><>]7<00><>,theseer-fdomdocument/css/DollarEqualRule.php"<22><>]p<><16><>&theseer-fdomdocument/css/RegexRule.php<68>"<22><>]<5D><00><><F9>s<81>'theseer-fdomdocument/css/Translator.php<68>"<22><>]<5D>F]hX<68>*theseer-fdomdocument/css/RuleInterface.php<68>"<22><>]<5D><00>,<2C><><97>)theseer-fdomdocument/css/NthChildRule.php<68>"<22><>]<5D>[4<><34><96>9symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpI"<22><>]I<00>(~<07>9symfony/polyfill-mbstring/Resources/unidata/upperCase.php<68>I"<22><>]<5D>I<00><><D2><01>?symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php9"<22><>]9>|zK<7A>'symfony/polyfill-mbstring/bootstrap.php"<22><>]<00><><15><>&symfony/polyfill-mbstring/Mbstring.php<68>k"<22><>]<5D>k1D<31><44><E8>8symfony/service-contracts/ServiceSubscriberInterface.php<68>"<22><>]<5D>SR<53>ܶ,symfony/service-contracts/ResetInterface.php<68>"<22><>]<5D><00>v<17><>4symfony/service-contracts/ServiceSubscriberTrait.php<68>"<22><>]<5D><00><03><>1symfony/service-contracts/ServiceLocatorTrait.php<68> "<22><>]<5D> <00><>.<2E><>5symfony/service-contracts/Test/ServiceLocatorTest.phpO "<22><>]O <00>!<21><><93>6symfony/service-contracts/ServiceProviderInterface.php<68>"<22><>]<5D><00>mX¶7symfony/console/Formatter/OutputFormatterStyleStack.php<68> "<22><>]<5D> <00>;2<><32>;symfony/console/Formatter/OutputFormatterStyleInterface.php<"<22><>]<<00><><9F>Z<D0>?symfony/console/Formatter/WrappableOutputFormatterInterface.php<68>"<22><>]<5D>`<60>K><3E>-symfony/console/Formatter/OutputFormatter.phpI"<22><>]IF<>G<FE>2symfony/console/Formatter/OutputFormatterStyle.php<68>"<22><>]<5D>6symfony/console/Formatter/OutputFormatterInterface.php<68>"<22><>]<5D>#7<>u<EA>(symfony/console/Input/InputInterface.php<68>"<22><>]<5D><00>Έڶ'symfony/console/Input/InputArgument.phpN "<22><>]N 4t<34><74><FF>-symfony/console/Input/InputAwareInterface.php:"<22><>]:<00> '<27><>$symfony/console/Input/ArrayInput.php<68>"<22><>]<5D>kq<6B>><3E>2symfony/console/Input/StreamableInputInterface.phpi"<22><>]i<00><17><><99>%symfony/console/Input/InputOption.php<68>"<22><>]<5D><0F>Ķ%symfony/console/Input/StringInput.phpL "<22><>]L l<> w<>symfony/console/Input/Input.phpr"<22><>]rU<>jx<6A>)symfony/console/Input/InputDefinition.php<68>+"<22><>]<5D>+Z<><5A>><3E>#symfony/console/Input/ArgvInput.php<68>-"<22><>]<5D>-<00><>SG<53>/symfony/console/Event/ConsoleTerminateEvent.php<68>"<22><>]<5D><0F>_<>+symfony/console/Event/ConsoleErrorEvent.php<68>"<22><>]<5D><00><>a<C4><61>-symfony/console/Event/ConsoleCommandEvent.php%"<22><>]%<><CBBE>&symfony/console/Event/ConsoleEvent.php<68>"<22><>]<5D>xS*<0F>=symfony/console/DependencyInjection/AddConsoleCommandPass.php)"<22><>])<00> 8~<7E>8symfony/console/Exception/NamespaceNotFoundException.php<68>"<22><>]<5D>BL<42>H<BB>,symfony/console/Exception/LogicException.php<68>"<22><>]<5D>SML<4D><4C>6symfony/console/Exception/CommandNotFoundException.php<68>"<22><>]<5D><00>N<03><>6symfony/console/Exception/InvalidArgumentException.php<68>"<22><>]<5D><00>u i<>0symfony/console/Exception/ExceptionInterface.php<68>"<22><>]<5D><00>l<E2><6C><F3>4symfony/console/Exception/InvalidOptionException.php<68>"<22><>]<5D><00><>;<13>.symfony/console/Exception/RuntimeException.php<68>"<22><>]<5D><17>*b<>(symfony/console/Tester/CommandTester.phpg "<22><>]g x<>3 <20>,symfony/console/Tester/ApplicationTester.phpW"<22><>]W6[E<>&symfony/console/Tester/TesterTrait.phpk"<22><>]k<00><>1<D3><31>(symfony/console/Output/ConsoleOutput.php<68>"<22><>]<5D><00>0<D0><30><91>%symfony/console/Output/NullOutput.phpn"<22><>]nt<>D<8F><44>!symfony/console/Output/Output.php<68>"<22><>]<5D><00>\j<17>1symfony/console/Output/ConsoleOutputInterface.php4"<22><>]4}<19><>)symfony/console/Output/BufferedOutput.phpH"<22><>]H<00>9<A0>Զ'symfony/console/Output/StreamOutput.php1"<22><>]1<00><46>/symfony/console/Output/ConsoleSectionOutput.phpD"<22><>]D<30><C798>*symfony/console/Output/OutputInterface.php$ "<22><>]$ <1D>w;<3B>)symfony/console/Command/LockableTrait.php<68>"<22><>]<5D><00><>u<75>#symfony/console/Command/Command.php<68>J"<22><>]<5D>J<00>#<23>6<92>'symfony/console/Command/ListCommand.php<68> "<22><>]<5D> <1E><>#<23>'symfony/console/Command/HelpCommand.php? "<22><>]? <00><><F8>'<27>!symfony/console/ConsoleEvents.php,"<22><>],~<7E>HG<48>$symfony/console/Helper/HelperSet.php<68> "<22><>]<5D> <00>3C <0C>+symfony/console/Helper/DescriptorHelper.php<68> "<22><>]<5D> <00><12><><FB>)symfony/console/Helper/TableSeparator.php"<22><>]&<26><> <0A>%symfony/console/Helper/TableStyle.php<68><"<22><>]<5D><<00>b<11>+symfony/console/Helper/InputAwareHelper.php<68>"<22><>]<5D><14><><17>/symfony/console/Helper/DebugFormatterHelper.phpH"<22><>]H]R`A<> symfony/console/Helper/Table.php"e"<22><>]"e<00><> ̶!symfony/console/Helper/Helper.php<68>"<22><>]<5D>}Ku<4B><75>$symfony/console/Helper/TableCell.php<68>"<22><>]<5D>B<><42>%<25>*symfony/console/Helper/FormatterHelper.php "<22><>] *+<2B><><C3>!symfony/console/Helper/Dumper.php<68>"<22><>]<5D>+'<27>f<9A>*symfony/console/Helper/HelperInterface.phpp"<22><>]p<00><><91>n<F1>(symfony/console/Helper/ProcessHelper.php<68>"<22><>]<5D><00>2C_<43>,symfony/console/Helper/ProgressIndicator.php|"<22><>]|Dُж$symfony/console/Helper/TableRows.phpG"<22><>]G<18>y<D9>&symfony/console/Helper/ProgressBar.phpq@"<22><>]q@<00>L2ж)symfony/console/Helper/QuestionHelper.php<68>;"<22><>]<5D>;<00> <0A><><FB>0symfony/console/Helper/SymfonyQuestionHelper.php<68> "<22><>]<5D> I(symfony/console/Logger/ConsoleLogger.php<68>"<22><>]<5D>r<>9<98><39>symfony/console/Terminal.phpW"<22><>]W<00>EpR<70>8symfony/console/CommandLoader/ContainerCommandLoader.php>"<22><>]>#<23><>*<2A>6symfony/console/CommandLoader/FactoryCommandLoader.php7"<22><>]7<00>m_<6D><5F>8symfony/console/CommandLoader/CommandLoaderInterface.php<68>"<22><>]<5D><00><><BA><12>symfony/console/Application.phpA<70>"<22><>]A<><00><><8E>z<AB>%symfony/console/Style/OutputStyle.php<68> "<22><>]<5D> 1kz<6B>&symfony/console/Style/SymfonyStyle.php<68>/"<22><>]<5D>/]<5D><><16>(symfony/console/Style/StyleInterface.php( "<22><>]( <00>5 <20><>/symfony/console/EventListener/ErrorListener.php<68>
"<22><>]<5D>
e?<3F>߶-symfony/console/Descriptor/TextDescriptor.php,1"<22><>],1 g-<2D><>5symfony/console/Descriptor/ApplicationDescription.phpm"<22><>]m<00><>]v<>2symfony/console/Descriptor/DescriptorInterface.php<68>"<22><>]<5D>P<>Z<DA><5A>)symfony/console/Descriptor/Descriptor.phpd "<22><>]d <00><><A1>T<E5>-symfony/console/Descriptor/JsonDescriptor.php"<22><>]<00><<3C>N<B9>1symfony/console/Descriptor/MarkdownDescriptor.php<68>"<22><>]<5D>f 0><3E>,symfony/console/Descriptor/XmlDescriptor.php<68>#"<22><>]<5D>#<00><><D3><7F>1symfony/console/Question/ConfirmationQuestion.php/"<22><>]/<00>ƞ<83><C69E>+symfony/console/Question/ChoiceQuestion.php"<22><>]<00>/<2F><><8C>%symfony/console/Question/Question.php<68>"<22><>]<5D><><7F>t<D2>.symfony/finder/Comparator/NumberComparator.php
"<22><>]
<0F><><B9>(symfony/finder/Comparator/Comparator.php"<22><>]~4<>ض,symfony/finder/Comparator/DateComparator.php<68>"<22><>]<5D><00>is<69>7symfony/finder/Exception/DirectoryNotFoundException.php<68>"<22><>]<5D><00>RI<>2symfony/finder/Exception/AccessDeniedException.php<68>"<22><>]<5D><00>cW޶symfony/finder/Gitignore.php{ "<22><>]{ <0A>Nm<4E>symfony/finder/SplFileInfo.php"<22><>]<00>L}<7D>symfony/finder/Finder.php<68>Z"<22><>]<5D>Z<00>1K׶3symfony/finder/Iterator/SizeRangeFilterIterator.php<68>"<22><>]<5D><03>0symfony/finder/Iterator/CustomFilterIterator.php<68>"<22><>]<5D>d)<29>4symfony/finder/Iterator/DepthRangeFilterIterator.php<68>"<22><>]<5D><05>h<BF><68>3symfony/finder/Iterator/DateRangeFilterIterator.php<68>"<22><>]<5D><00><><83>)<29>6symfony/finder/Iterator/MultiplePcreFilterIterator.php{ "<22><>]{ <03>ӹ<CC>6symfony/finder/Iterator/RecursiveDirectoryIterator.php<68>"<22><>]<5D><04><>c<B2>.symfony/finder/Iterator/PathFilterIterator.php<68>"<22><>]<5D>}<7D><><14>,symfony/finder/Iterator/SortableIterator.php<68> "<22><>]<5D> 1<><1F><>2symfony/finder/Iterator/FilenameFilterIterator.php<68>"<22><>]<5D> <0C>p<CB><70>:symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php<68> "<22><>]<5D> d<>!!<21>5symfony/finder/Iterator/FilecontentFilterIterator.php<68>"<22><>]<5D>r<>~<7E>2symfony/finder/Iterator/FileTypeFilterIterator.phpC"<22><>]CQN<51><4E><B6>symfony/finder/Glob.php<68>"<22><>]<5D><00><><99>{<7B> symfony/polyfill-ctype/Ctype.php}"<22><>]}\<5C>$symfony/polyfill-ctype/bootstrap.phpS"<22><>]S<00><>7<>8symfony/polyfill-php73/Resources/stubs/JsonException.php"<22><>]<F<><46>$symfony/polyfill-php73/bootstrap.php"<22><>]`<60><><D6><F8> symfony/polyfill-php73/Php73.phpg"<22><>]g/<2F><>n<C6>.sebastian/comparator/src/ComparisonFailure.php<68> "<22><>]<5D> <1C>I<8D>,sebastian/comparator/src/ArrayComparator.php<68>"<22><>]<5D>Lj:Q<>$sebastian/comparator/src/Factory.php "<22><>] o<16><><AF>+sebastian/comparator/src/TypeComparator.php<68>"<22><>]<5D>S<><53>Ҷ/sebastian/comparator/src/ResourceComparator.php."<22><>].<00>K<B0><15>.sebastian/comparator/src/NumericComparator.php<68>"<22><>]<5D>=<3D><>Զ7sebastian/comparator/src/SplObjectStorageComparator.php<68>"<22><>]<5D> <04><12>'sebastian/comparator/src/Comparator.php\"<22><>]\8<>9<>/sebastian/comparator/src/DateTimeComparator.php<68> "<22><>]<5D> <00>~<7E>ȶ-sebastian/comparator/src/DoubleComparator.phpt"<22><>]t<00>^k<><6B>0sebastian/comparator/src/ExceptionComparator.php<68>"<22><>]<5D>ѕ<><D195><BC>-sebastian/comparator/src/ScalarComparator.php<68> "<22><>]<5D> QM<51><4D>-sebastian/comparator/src/ObjectComparator.php"<22><>]<00>9<CE>2<A7>1sebastian/comparator/src/MockObjectComparator.phpP"<22><>]P<00>PN<>.sebastian/comparator/src/DOMNodeComparator.php<68>
"<22><>]<5D>
8<><15><>1sebastian/code-unit-reverse-lookup/src/Wizard.phpe "<22><>]e <00><><FE><ED><8E>8sebastian/resource-operations/src/ResourceOperations.phpi"<22><>]i6G<0E>sebastian/diff/src/Differ.php9%"<22><>]9%<00><>D<18>sebastian/diff/src/Diff.php<68>"<22><>]<5D><33><DFAC>sebastian/diff/src/Parser.php<68> "<22><>]<5D> G<16>2<9C>sebastian/diff/src/Line.phpO"<22><>]O'<27><> <09>Fsebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php "<22><>] v<><<00>sebastian/diff/src/Chunk.phpm"<22><>]m<00>A<D3><02>Hsebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.phpV"<22><>]Vfն9sebastian/diff/src/LongestCommonSubsequenceCalculator.php<"<22><>]<<11><><F7><9F>-sebastian/finder-facade/src/Configuration.php<68> "<22><>]<5D> Xt&<26>,sebastian/finder-facade/src/FinderFacade.php "<22><>] O<>z<9F><7A>'sebastian/global-state/src/Restorer.php"<22><>]6<><36>k<F0>+sebastian/global-state/src/CodeExporter.phpj "<22><>]j 7g<37>Ѷ'sebastian/global-state/src/Snapshot.php>)"<22><>]>)<00>)<29>ɶ(sebastian/global-state/src/Blacklist.php<68>
"<22><>]<5D>
=ŵ@<40>#sebastian/exporter/src/Exporter.phpM#"<22><>]M#ғ<><D293><B2>2sebastian/object-reflector/src/ObjectReflector.php<68>"<22><>]<5D><00> "<19>,sebastian/object-reflector/src/Exception.phpN"<22><>]N<00> ^<5E><>;sebastian/object-reflector/src/InvalidArgumentException.php<68>"<22><>]<5D>Y<0E>J<C4>!sebastian/type/src/ObjectType.php<68>"<22><>]<5D>Ř1߶(sebastian/type/src/GenericObjectType.php<68>"<22><>]<5D><00><><FB><13>"sebastian/type/src/UnknownType.phpE"<22><>]E<00> <0B><><BB>sebastian/type/src/Type.php<68>"<22><>]<5D>k<><6B>5<8E>sebastian/type/src/NullType.php["<22><>][9<>o<D5><6F>sebastian/type/src/TypeName.php"<22><>]<00>N<CE>><3E>#sebastian/type/src/IterableType.phpQ"<22><>]Q<00><><B3>q<AD>!sebastian/type/src/SimpleType.php<68>"<22><>]<5D>g<><67> <0C>#sebastian/type/src/CallableType.php<68>"<22><>]<5D>Ί]<5D><>sebastian/type/src/VoidType.php["<22><>][)T<>!sebastian/version/src/Version.php<68>"<22><>]<5D>N%sebastian/environment/src/Console.php<68>"<22><>]<5D>E<><45>ܶ-sebastian/environment/src/OperatingSystem.php<68>"<22><>]<5D><00>`<60><><AB>%sebastian/environment/src/Runtime.phpd"<22><>]d<1C>-sebastian/recursion-context/src/Exception.phpJ"<22><>]J<00><><C8><F4><B3><sebastian/recursion-context/src/InvalidArgumentException.php<68>"<22><>]<5D>mH<>+sebastian/recursion-context/src/Context.php{"<22><>]{<00><89><7F><A4>-sebastian/object-enumerator/src/Exception.php6"<22><>]6n$*a<><sebastian/object-enumerator/src/InvalidArgumentException.phpx"<22><>]x<00><>'<27><>.sebastian/object-enumerator/src/Enumerator.phpr"<22><>]rz<>\<5C><><?php declare(strict_types=1);
/*
* This file is part of phpcov.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\PHPCOV;
use PHPUnit\Util\Configuration;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport;
use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport;
use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport;
use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport;
use SebastianBergmann\CodeCoverage\Report\Text as TextReport;
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport;
use Symfony\Component\Console\Command\Command as AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
abstract class BaseCommand extends AbstractCommand
{
protected function handleConfiguration(CodeCoverage $coverage, InputInterface $input): void
{
$configuration = $input->getOption('configuration');
if (!$configuration) {
return;
}
$filter = $coverage->filter();
$configuration = Configuration::getInstance($configuration);
$filterConfiguration = $configuration->getFilterConfiguration();
$coverage->setAddUncoveredFilesFromWhitelist(
$filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist']
);
$coverage->setProcessUncoveredFilesFromWhitelist(
$filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist']
);
foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) {
$filter->addDirectoryToWhitelist(
$dir['path'],
$dir['suffix'],
$dir['prefix']
);
}
foreach ($filterConfiguration['whitelist']['include']['file'] as $file) {
$filter->addFileToWhitelist($file);
}
foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) {
$filter->removeDirectoryFromWhitelist(
$dir['path'],
$dir['suffix'],
$dir['prefix']
);
}
foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) {
$filter->removeFileFromWhitelist($file);
}
}
protected function handleFilter(CodeCoverage $coverage, InputInterface $input): void
{
$filter = $coverage->filter();
$coverage->setAddUncoveredFilesFromWhitelist(
$input->getOption('add-uncovered')
);
$coverage->setProcessUncoveredFilesFromWhitelist(
$input->getOption('process-uncovered')
);
foreach ($input->getOption('whitelist') as $item) {
if (\is_dir($item)) {
$filter->addDirectoryToWhitelist($item);
} elseif (\is_file($item)) {
$filter->addFileToWhitelist($item);
}
}
}
protected function handleReports(CodeCoverage $coverage, InputInterface $input, OutputInterface $output): void
{
if ($input->getOption('clover')) {
$output->write("\nGenerating code coverage report in Clover XML format ...");
$writer = new CloverReport;
$writer->process($coverage, $input->getOption('clover'));
$output->write(" done\n");
}
if ($input->getOption('crap4j')) {
$output->write("\nGenerating code coverage report in Crap4J XML format...");
$writer = new Crap4jReport;
$writer->process($coverage, $input->getOption('crap4j'));
$output->write(" done\n");
}
if ($input->getOption('html')) {
$output->write("\nGenerating code coverage report in HTML format ...");
$writer = new HtmlReport;
$writer->process($coverage, $input->getOption('html'));
$output->write(" done\n");
}
if ($input->getOption('php')) {
$output->write("\nGenerating code coverage report in PHP format ...");
$writer = new PhpReport;
$writer->process($coverage, $input->getOption('php'));
$output->write(" done\n");
}
if ($input->getOption('text')) {
$report = new TextReport;
$color = false;
if ($input->getOption('ansi')) {
$color = true;
}
$output->write($report->process($coverage, $color));
}
if ($input->getOption('xml')) {
$output->write(
"\nGenerating code coverage report in PHP format ..."
);
$writer = new XmlReport('unknown');
$writer->process($coverage, $input->getOption('xml'));
$output->write(" done\n");
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of phpcov.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\PHPCOV;
use SebastianBergmann\Diff\Line;
use SebastianBergmann\Diff\Parser as DiffParser;
class PatchCoverage
{
/**
* @param string $coverage
* @param string $patch
* @param string $prefix
*
* @return array
*/
public function execute($coverage, $patch, $prefix)
{
$result = [
'numChangedLinesThatAreExecutable' => 0,
'numChangedLinesThatWereExecuted' => 0,
'changedLinesThatWereNotExecuted' => [],
];
if (\substr($prefix, -1, 1) != \DIRECTORY_SEPARATOR) {
$prefix .= \DIRECTORY_SEPARATOR;
}
$coverage = include($coverage);
$coverage = $coverage->getData();
$parser = new DiffParser;
$patch = $parser->parse(\file_get_contents($patch));
$changes = [];
foreach ($patch as $diff) {
$file = \substr($diff->getTo(), 2);
$changes[$file] = [];
foreach ($diff->getChunks() as $chunk) {
$lineNr = $chunk->getEnd();
foreach ($chunk->getLines() as $line) {
if ($line->getType() == Line::ADDED) {
$changes[$file][] = $lineNr;
}
if ($line->getType() != Line::REMOVED) {
$lineNr++;
}
}
}
}
foreach ($changes as $file => $lines) {
$key = $prefix . $file;
foreach ($lines as $line) {
if (isset($coverage[$key][$line]) &&
\is_array($coverage[$key][$line])) {
$result['numChangedLinesThatAreExecutable']++;
if (empty($coverage[$key][$line])) {
if (!isset($result['changedLinesThatWereNotExecuted'][$file])) {
$result['changedLinesThatWereNotExecuted'][$file] = [];
}
$result['changedLinesThatWereNotExecuted'][$file][] = $line;
} else {
$result['numChangedLinesThatWereExecuted']++;
}
}
}
}
return $result;
}
}
<?php declare(strict_types=1);
/*
* This file is part of phpcov.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\PHPCOV;
use SebastianBergmann\Version;
use Symfony\Component\Console\Application as AbstractApplication;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* TextUI frontend for php-code-coverage.
*/
class Application extends AbstractApplication
{
public function __construct()
{
$version = new Version('6.0.1', \dirname(__DIR__));
parent::__construct('phpcov', $version->getVersion());
$this->add(new ExecuteCommand);
$this->add(new MergeCommand);
$this->add(new PatchCoverageCommand);
}
/**
* Runs the current application.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
* @return int 0 if everything went fine, or an error code
*/
public function doRun(InputInterface $input, OutputInterface $output)
{
if (!$input->hasParameterOption('--quiet')) {
$output->write(
\sprintf(
"phpcov %s by Sebastian Bergmann.\n\n",
$this->getVersion()
)
);
}
if ($input->hasParameterOption('--version') ||
$input->hasParameterOption('-V')) {
exit;
}
parent::doRun($input, $output);
}
}
<?php declare(strict_types=1);
/*
* This file is part of phpcov.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\PHPCOV;
use SebastianBergmann\CodeCoverage\Util;
use Symfony\Component\Console\Command\Command as AbstractCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class PatchCoverageCommand extends AbstractCommand
{
/**
* Configures the current command.
*/
protected function configure(): void
{
$this->setName('patch-coverage')
->addArgument(
'coverage',
InputArgument::REQUIRED,
'Exported code coverage object'
)
->addOption(
'patch',
null,
InputOption::VALUE_REQUIRED,
'Unified diff to be analysed for patch coverage'
)
->addOption(
'path-prefix',
null,
InputOption::VALUE_REQUIRED,
'Prefix that needs to be stripped from paths in the diff'
);
}
/**
* Executes the current command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return null|int null or 0 if everything went fine, or an error code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$pc = new PatchCoverage;
$pc = $pc->execute(
$input->getArgument('coverage'),
$input->getOption('patch'),
$input->getOption('path-prefix')
);
$output->writeln(
\sprintf(
'%d / %d changed executable lines covered (%s)',
$pc['numChangedLinesThatWereExecuted'],
$pc['numChangedLinesThatAreExecutable'],
Util::percent(
$pc['numChangedLinesThatWereExecuted'],
$pc['numChangedLinesThatAreExecutable'],
true
)
)
);
if (!empty($pc['changedLinesThatWereNotExecuted'])) {
$output->writeln("\nChanged executable lines that are not covered:\n");
foreach ($pc['changedLinesThatWereNotExecuted'] as $file => $lines) {
foreach ($lines as $line) {
$output->writeln(
\sprintf(
' %s:%d',
$file,
$line
)
);
}
}
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of phpcov.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\PHPCOV;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class ExecuteCommand extends BaseCommand
{
/**
* Configures the current command.
*/
protected function configure(): void
{
$this->setName('execute')
->addArgument(
'script',
InputArgument::REQUIRED,
'Script to execute'
)
->addOption(
'configuration',
null,
InputOption::VALUE_REQUIRED,
'Read configuration from XML file'
)
->addOption(
'whitelist',
null,
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Add directory or file to the whitelist'
)
->addOption(
'add-uncovered',
null,
InputOption::VALUE_NONE,
'Add whitelisted files that are not covered'
)
->addOption(
'process-uncovered',
null,
InputOption::VALUE_NONE,
'Process whitelisted files that are not covered'
)
->addOption(
'clover',
null,
InputOption::VALUE_REQUIRED,
'Generate code coverage report in Clover XML format'
)
->addOption(
'crap4j',
null,
InputOption::VALUE_REQUIRED,
'Generate code coverage report in Crap4J XML format'
)
->addOption(
'html',
null,
InputOption::VALUE_REQUIRED,
'Generate code coverage report in HTML format'
)
->addOption(
'php',
null,
InputOption::VALUE_REQUIRED,
'Export code coverage object to file'
)
->addOption(
'text',
null,
InputOption::VALUE_NONE,
'Write code coverage report in text format to STDOUT'
)
->addOption(
'xml',
null,
InputOption::VALUE_REQUIRED,
'Generate code coverage report in PHPUnit XML format'
);
}
/**
* Executes the current command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return null|int null or 0 if everything went fine, or an error code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$coverage = new CodeCoverage;
$this->handleConfiguration($coverage, $input);
$this->handleFilter($coverage, $input);
$coverage->start('phpcov');
require $input->getArgument('script');
$coverage->stop();
$this->handleReports($coverage, $input, $output);
}
}
<?php declare(strict_types=1);
/*
* This file is part of phpcov.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\PHPCOV;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\FinderFacade\FinderFacade;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class MergeCommand extends BaseCommand
{
/**
* @var string[]
*/
protected $mergeErrors;
/**
* Configures the current command.
*/
protected function configure(): void
{
$this->setName('merge')
->addArgument(
'directory',
InputArgument::REQUIRED,
'Directory to scan for exported code coverage objects stored in .cov files'
)
->addOption(
'clover',
null,
InputOption::VALUE_REQUIRED,
'Generate code coverage report in Clover XML format'
)
->addOption(
'crap4j',
null,
InputOption::VALUE_REQUIRED,
'Generate code coverage report in Crap4J XML format'
)
->addOption(
'html',
null,
InputOption::VALUE_REQUIRED,
'Generate code coverage report in HTML format'
)
->addOption(
'php',
null,
InputOption::VALUE_REQUIRED,
'Export code coverage object to file'
)
->addOption(
'text',
null,
InputOption::VALUE_NONE,
'Generate code coverage report in text format to STDOUT'
)
->addOption(
'xml',
null,
InputOption::VALUE_REQUIRED,
'Generate code coverage report in PHPUnit XML format'
);
}
/**
* Executes the current command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return null|int null or 0 if everything went fine, or an error code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$mergedCoverage = new CodeCoverage;
$finder = new FinderFacade(
[$input->getArgument('directory')],
[],
['*.cov']
);
foreach ($finder->findFiles() as $file) {
$_coverage = include($file);
if (!($_coverage instanceof CodeCoverage)) {
$this->mergeErrors[] = $file;
unset($_coverage);
continue;
}
$mergedCoverage->merge($_coverage);
unset($_coverage);
}
$this->handleReports($mergedCoverage, $input, $output);
$this->outputMergeErrors($output);
}
protected function outputMergeErrors(OutputInterface $output): void
{
if (empty($this->mergeErrors)) {
return;
}
foreach ($this->mergeErrors as $mergeError) {
$output->write('Failed to merge: ' . $mergeError, true);
}
}
}
phpunit/phpcov: 6.0.1
doctrine/instantiator: 1.3.0
myclabs/deep-copy: 1.9.3
phar-io/manifest: 1.0.3
phar-io/version: 2.0.1
phpdocumentor/reflection-common: 2.0.0
phpdocumentor/reflection-docblock: 4.3.2
phpdocumentor/type-resolver: 1.0.1
phpspec/prophecy: 1.9.0
phpunit/php-code-coverage: 7.0.8
phpunit/php-file-iterator: 2.0.2
phpunit/php-text-template: 1.2.1
phpunit/php-timer: 2.1.2
phpunit/php-token-stream: 3.1.1
phpunit/phpunit: 8.4.3
psr/container: 1.0.0
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.2
sebastian/finder-facade: 1.2.2
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/type: 1.1.3
sebastian/version: 2.0.1
symfony/console: v4.3.8
symfony/finder: v4.3.8
symfony/polyfill-ctype: v1.12.0
symfony/polyfill-mbstring: v1.12.0
symfony/polyfill-php73: v1.12.0
symfony/service-contracts: v1.1.8
theseer/fdomdocument: 1.6.6
theseer/tokenizer: 1.1.3
webmozart/assert: 1.5.0
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;
class TokenCollection implements \ArrayAccess, \Iterator, \Countable {
/**
* @var Token[]
*/
private $tokens = [];
/**
* @var int
*/
private $pos;
/**
* @param Token $token
*/
public function addToken(Token $token) {
$this->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]);
}
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;
class NamespaceUriException extends Exception {
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;
class Exception extends \Exception {
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;
class NamespaceUri {
/** @var string */
private $value;
/**
* @param string $value
*/
public function __construct(string $value) {
$this->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)
);
}
}
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;
use DOMDocument;
class XMLSerializer {
/**
* @var \XMLWriter
*/
private $writer;
/**
* @var Token
*/
private $previousToken;
/**
* @var NamespaceUri
*/
private $xmlns;
/**
* XMLSerializer constructor.
*
* @param NamespaceUri $xmlns
*/
public function __construct(NamespaceUri $xmlns = null) {
if ($xmlns === null) {
$xmlns = new NamespaceUri('https://github.com/theseer/tokenizer');
}
$this->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());
if (count($tokens) > 0) {
$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();
}
}
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;
class TokenCollectionException extends Exception {
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;
class Tokenizer {
/**
* Token Map for "non-tokens"
*
* @var array
*/
private $map = [
'(' => '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;
}
}
<?php declare(strict_types = 1);
namespace TheSeer\Tokenizer;
class Token {
/**
* @var int
*/
private $line;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $value;
/**
* Token constructor.
*
* @param int $line
* @param string $name
* @param string $value
*/
public function __construct(int $line, string $name, string $value) {
$this->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;
}
}
<?php
/*
* This file is part of the Text_Template package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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
)
)
);
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-timer.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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(float $bytes): string
{
foreach (self::$sizes as $unit => $value) {
if ($bytes >= $value) {
return \sprintf('%.2f %s', $bytes >= 1024 ? $bytes / $value : $bytes, $unit);
}
}
return $bytes . ' byte' . ((int) $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))
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-timer.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Timer;
interface Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-timer.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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
{
}
<?php
/*
* This file is part of php-file-iterator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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);
}
}
<?php
/*
* This file is part of php-file-iterator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php
/*
* This file is part of php-file-iterator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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 declare(strict_types=1);
/*
* This file is part of php-token-stream.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
final class PHP_Token_Util
{
public static function getClass($object): string
{
$parts = explode('\\', get_class($object));
return array_pop($parts);
}
}<?php
/*
* This file is part of php-token-stream.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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 = [];
}
}
}
<?php
/*
* This file is part of php-token-stream.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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 (PHP_Token_Util::getClass($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 (PHP_Token_Util::getClass($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->getName() !== null) {
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;
}
}
}
<?php
/*
* This file is part of php-token-stream.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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_', '', PHP_Token_Util::getClass($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_', '', PHP_Token_Util::getClass($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_', '', PHP_Token_Util::getClass($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 (PHP_Token_Util::getClass($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
{
}
// Tokens introduced in PHP 7.4
class PHP_Token_COALESCE_EQUAL extends PHP_Token
{
}
class PHP_Token_FN extends PHP_Token
{
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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) {
$this->report = (new 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'];
$lastMethodEndLine = $firstMethod['endLine'];
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:
case \PHP_Token_USE_FUNCTION::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;
}
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 = [];
foreach ($this->driver->stop() 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);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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.8', \dirname(__DIR__));
self::$version = $version->getVersion();
}
return self::$version;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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,
*
* <code>
* Array
* (
* [Money.php] => Array
* (
* ...
* )
*
* [MoneyBag.php] => Array
* (
* ...
* )
* )
* </code>
*
* is transformed into
*
* <code>
* Array
* (
* [.] => Array
* (
* [Money.php] => Array
* (
* ...
* )
*
* [MoneyBag.php] => Array
* (
* ...
* )
* )
* )
* </code>
*/
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,
*
* <code>
* Array
* (
* [/home/sb/Money/Money.php] => Array
* (
* ...
* )
*
* [/home/sb/Money/MoneyBag.php] => Array
* (
* ...
* )
* )
* </code>
*
* is reduced to
*
* <code>
* Array
* (
* [Money.php] => Array
* (
* ...
* )
*
* [MoneyBag.php] => Array
* (
* ...
* )
* )
* </code>
*/
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);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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'],
];
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><build/><project/></phpunit>');
$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);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><file /></phpunit>');
$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);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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
{
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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']]);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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));
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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))
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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 = '&nbsp;/&nbsp;';
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(
' <li class="breadcrumb-item active">%s</li>' . "\n",
$node->getName()
);
if ($node instanceof DirectoryNode) {
$buffer .= ' <li class="breadcrumb-item">(<a href="dashboard.html">Dashboard</a>)</li>' . "\n";
}
return $buffer;
}
protected function getInactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string
{
return \sprintf(
' <li class="breadcrumb-item"><a href="%sindex.html">%s</a></li>' . "\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(
'<a href="%s" target="_top">%s %s</a>',
$runtime->getVendorUrl(),
$runtime->getName(),
$runtime->getVersion()
);
if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) {
$buffer .= \sprintf(
' with <a href="https://xdebug.org/">Xdebug %s</a>',
\phpversion('xdebug')
);
}
if ($runtime->hasPCOV() && !$runtime->hasPHPDBGCodeCoverage()) {
$buffer .= \sprintf(
' with <a href="https://github.com/krakjoe/pcov">PCOV %s</a>',
\phpversion('pcov')
);
}
return $buffer;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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(
'<a href="%s/index.html">%s</a>',
$node->getName(),
$node->getName()
);
$up = \str_repeat('../', \count($node->getPathAsArray()) - 2);
$data['icon'] = \sprintf('<img src="%s_icons/file-directory.svg" class="octicon" />', $up);
} else {
$data['name'] = \sprintf(
'<a href="%s.html">%s</a>',
$node->getName(),
$node->getName()
);
$up = \str_repeat('../', \count($node->getPathAsArray()) - 2);
$data['icon'] = \sprintf('<img src="%s_icons/file-code.svg" class="octicon" />', $up);
}
}
return $this->renderItemTemplate(
new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'),
$data
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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(
'<a href="%s">%s</a>',
\str_replace($baseLink, '', $method['link']),
$methodName
),
];
}
$result['class'][] = [
$class['coverage'],
$class['ccn'],
\sprintf(
'<a href="%s">%s</a>',
\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(
' <tr><td><a href="%s">%s</a></td><td class="text-right">%d%%</td></tr>' . "\n",
\str_replace($baseLink, '', $classes[$className]['link']),
$className,
$coverage
);
}
foreach ($leastTestedMethods as $methodName => $coverage) {
[$class, $method] = \explode('::', $methodName);
$result['method'] .= \sprintf(
' <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d%%</td></tr>' . "\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(
' <tr><td><a href="%s">%s</a></td><td class="text-right">%d</td></tr>' . "\n",
\str_replace($baseLink, '', $classes[$className]['link']),
$className,
$crap
);
}
foreach ($methodRisks as $methodName => $crap) {
[$class, $method] = \explode('::', $methodName);
$result['method'] .= \sprintf(
' <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d</td></tr>' . "\n",
\str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
$methodName,
$method,
$crap
);
}
return $result;
}
protected function getActiveBreadcrumb(AbstractNode $node): string
{
return \sprintf(
' <li class="breadcrumb-item"><a href="index.html">%s</a></li>' . "\n" .
' <li class="breadcrumb-item active">(Dashboard)</li>' . "\n",
$node->getName()
);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Code Coverage for {{full_path}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/octicons.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
</head>
<body>
<header>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
{{breadcrumbs}}
</ol>
</nav>
</div>
</div>
</div>
</header>
<div class="container-fluid">
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<td>&nbsp;</td>
<td colspan="10"><div align="center"><strong>Code Coverage</strong></div></td>
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
<td colspan="4"><div align="center"><strong>Functions and Methods</strong></div></td>
<td colspan="3"><div align="center"><strong>Lines</strong></div></td>
</tr>
</thead>
<tbody>
{{items}}
</tbody>
</table>
</div>
<table id="code" class="table table-borderless table-condensed">
<tbody>
{{lines}}
</tbody>
</table>
<footer>
<hr/>
<h4>Legend</h4>
<p>
<span class="success"><strong>Executed</strong></span>
<span class="danger"><strong>Not Executed</strong></span>
<span class="warning"><strong>Dead Code</strong></span>
</p>
<p>
<small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
</p>
<a title="Back to the top" id="toplink" href="#">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M12 11L6 5l-6 6h12z"/></svg>
</a>
</footer>
</div>
<script src="{{path_to_root}}_js/jquery.min.js" type="text/javascript"></script>
<script src="{{path_to_root}}_js/popper.min.js" type="text/javascript"></script>
<script src="{{path_to_root}}_js/bootstrap.min.js" type="text/javascript"></script>
<script src="{{path_to_root}}_js/file.js" type="text/javascript"></script>
</body>
</html>
<tr>
<td class="{{lines_level}}">{{icon}}{{name}}</td>
<td class="{{lines_level}} big">{{lines_bar}}</td>
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
<td class="{{methods_level}} big">{{methods_bar}}</td>
<td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
<td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
<td class="{{classes_level}} big">{{classes_bar}}</td>
<td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
<td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
</tr>
<tr>
<td class="{{methods_level}}" colspan="4">{{name}}</td>
<td class="{{methods_level}} big">{{methods_bar}}</td>
<td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
<td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
<td class="{{methods_level}} small">{{crap}}</td>
<td class="{{lines_level}} big">{{lines_bar}}</td>
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
</tr>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M8.5 1H1c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V4.5L8.5 1zM11 14H1V2h7l3 3v9zM5 6.98L3.5 8.5 5 10l-.5 1L2 8.5 4.5 6l.5.98zM7.5 6L10 8.5 7.5 11l-.5-.98L8.5 8.5 7 7l.5-1z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M13 4H7V3c0-.66-.31-1-1-1H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zM6 4H1V3h5v1z"/></svg><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dashboard for {{full_path}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/nv.d3.min.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
</head>
<body>
<header>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
{{breadcrumbs}}
</ol>
</nav>
</div>
</div>
</div>
</header>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<h2>Classes</h2>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h3>Coverage Distribution</h3>
<div id="classCoverageDistribution" style="height: 300px;">
<svg></svg>
</div>
</div>
<div class="col-md-6">
<h3>Complexity</h3>
<div id="classComplexity" style="height: 300px;">
<svg></svg>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h3>Insufficient Coverage</h3>
<div class="scrollbox">
<table class="table">
<thead>
<tr>
<th>Class</th>
<th class="text-right">Coverage</th>
</tr>
</thead>
<tbody>
{{insufficient_coverage_classes}}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h3>Project Risks</h3>
<div class="scrollbox">
<table class="table">
<thead>
<tr>
<th>Class</th>
<th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
</tr>
</thead>
<tbody>
{{project_risks_classes}}
</tbody>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Methods</h2>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h3>Coverage Distribution</h3>
<div id="methodCoverageDistribution" style="height: 300px;">
<svg></svg>
</div>
</div>
<div class="col-md-6">
<h3>Complexity</h3>
<div id="methodComplexity" style="height: 300px;">
<svg></svg>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h3>Insufficient Coverage</h3>
<div class="scrollbox">
<table class="table">
<thead>
<tr>
<th>Method</th>
<th class="text-right">Coverage</th>
</tr>
</thead>
<tbody>
{{insufficient_coverage_methods}}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h3>Project Risks</h3>
<div class="scrollbox">
<table class="table">
<thead>
<tr>
<th>Method</th>
<th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
</tr>
</thead>
<tbody>
{{project_risks_methods}}
</tbody>
</table>
</div>
</div>
</div>
<footer>
<hr/>
<p>
<small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
</p>
</footer>
</div>
<script src="{{path_to_root}}_js/jquery.min.js" type="text/javascript"></script>
<script src="{{path_to_root}}_js/d3.min.js" type="text/javascript"></script>
<script src="{{path_to_root}}_js/nv.d3.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
nv.addGraph(function() {
var chart = nv.models.multiBarChart();
chart.tooltips(false)
.showControls(false)
.showLegend(false)
.reduceXTicks(false)
.staggerLabels(true)
.yAxis.tickFormat(d3.format('d'));
d3.select('#classCoverageDistribution svg')
.datum(getCoverageDistributionData({{class_coverage_distribution}}, "Class Coverage"))
.transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
nv.addGraph(function() {
var chart = nv.models.multiBarChart();
chart.tooltips(false)
.showControls(false)
.showLegend(false)
.reduceXTicks(false)
.staggerLabels(true)
.yAxis.tickFormat(d3.format('d'));
d3.select('#methodCoverageDistribution svg')
.datum(getCoverageDistributionData({{method_coverage_distribution}}, "Method Coverage"))
.transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function getCoverageDistributionData(data, label) {
var labels = [
'0%',
'0-10%',
'10-20%',
'20-30%',
'30-40%',
'40-50%',
'50-60%',
'60-70%',
'70-80%',
'80-90%',
'90-100%',
'100%'
];
var values = [];
$.each(labels, function(key) {
values.push({x: labels[key], y: data[key]});
});
return [
{
key: label,
values: values,
color: "#4572A7"
}
];
}
nv.addGraph(function() {
var chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.showLegend(false)
.forceX([0, 100]);
chart.tooltipContent(function(graph) {
return '<p>' + graph.point.class + '</p>';
});
chart.xAxis.axisLabel('Code Coverage (in percent)');
chart.yAxis.axisLabel('Cyclomatic Complexity');
d3.select('#classComplexity svg')
.datum(getComplexityData({{complexity_class}}, 'Class Complexity'))
.transition()
.duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
nv.addGraph(function() {
var chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.showLegend(false)
.forceX([0, 100]);
chart.tooltipContent(function(graph) {
return '<p>' + graph.point.class + '</p>';
});
chart.xAxis.axisLabel('Code Coverage (in percent)');
chart.yAxis.axisLabel('Method Complexity');
d3.select('#methodComplexity svg')
.datum(getComplexityData({{complexity_method}}, 'Method Complexity'))
.transition()
.duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function getComplexityData(data, label) {
var values = [];
$.each(data, function(key) {
var value = Math.round(data[key][0]*100) / 100;
values.push({
x: value,
y: data[key][1],
class: data[key][2],
size: 0.05,
shape: 'diamond'
});
});
return [
{
key: label,
values: values,
color: "#4572A7"
}
];
}
});
</script>
</body>
</html>
<div class="progress">
<div class="progress-bar bg-{{level}}" role="progressbar" aria-valuenow="{{percent}}" aria-valuemin="0" aria-valuemax="100" style="width: {{percent}}%">
<span class="sr-only">{{percent}}% covered ({{level}})</span>
</div>
</div>
<tr>
<td class="{{classes_level}}">{{name}}</td>
<td class="{{classes_level}} big">{{classes_bar}}</td>
<td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
<td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
<td class="{{methods_level}} big">{{methods_bar}}</td>
<td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
<td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
<td class="{{methods_level}} small">{{crap}}</td>
<td class="{{lines_level}} big">{{lines_bar}}</td>
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
</tr>
.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.1 (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 */<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Code Coverage for {{full_path}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/octicons.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
</head>
<body>
<header>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
{{breadcrumbs}}
</ol>
</nav>
</div>
</div>
</div>
</header>
<div class="container-fluid">
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<td>&nbsp;</td>
<td colspan="9"><div align="center"><strong>Code Coverage</strong></div></td>
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"><div align="center"><strong>Lines</strong></div></td>
<td colspan="3"><div align="center"><strong>Functions and Methods</strong></div></td>
<td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
</tr>
</thead>
<tbody>
{{items}}
</tbody>
</table>
</div>
<footer>
<hr/>
<h4>Legend</h4>
<p>
<span class="danger"><strong>Low</strong>: 0% to {{low_upper_bound}}%</span>
<span class="warning"><strong>Medium</strong>: {{low_upper_bound}}% to {{high_lower_bound}}%</span>
<span class="success"><strong>High</strong>: {{high_lower_bound}}% to 100%</span>
</p>
<p>
<small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
</p>
</footer>
</div>
</body>
</html>
/*!
* Bootstrap v4.3.1 (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;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function s(t,e,n){return e&&i(t.prototype,e),n&&i(t,n),t}function l(o){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},e=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(e=e.concat(Object.getOwnPropertySymbols(r).filter(function(t){return Object.getOwnPropertyDescriptor(r,t).enumerable}))),e.forEach(function(t){var e,n,i;e=o,i=r[n=t],n in e?Object.defineProperty(e,n,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[n]=i})}return o}g=g&&g.hasOwnProperty("default")?g.default:g,u=u&&u.hasOwnProperty("default")?u.default:u;var e="transitionend";function n(t){var e=this,n=!1;return g(this).one(_.TRANSITION_END,function(){n=!0}),setTimeout(function(){n||_.triggerTransitionEnd(e)},t),this}var _={TRANSITION_END:"bsTransitionEnd",getUID:function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var e=g(t).css("transition-duration"),n=g(t).css("transition-delay"),i=parseFloat(e),o=parseFloat(n);return i||o?(e=e.split(",")[0],n=n.split(",")[0],1e3*(parseFloat(e)+parseFloat(n))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){g(t).trigger(e)},supportsTransitionEnd:function(){return Boolean(e)},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],r=e[i],s=r&&_.isElement(r)?"element":(a=r,{}.toString.call(a).match(/\s([a-z]+)/i)[1].toLowerCase());if(!new RegExp(o).test(s))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+s+'" but expected type "'+o+'".')}var a},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"!=typeof t.getRootNode)return t instanceof ShadowRoot?t:t.parentNode?_.findShadowRoot(t.parentNode):null;var e=t.getRootNode();return e instanceof ShadowRoot?e:null}};g.fn.emulateTransitionEnd=n,g.event.special[_.TRANSITION_END]={bindType:e,delegateType:e,handle:function(t){if(g(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var o="alert",r="bs.alert",a="."+r,c=g.fn[o],h={CLOSE:"close"+a,CLOSED:"closed"+a,CLICK_DATA_API:"click"+a+".data-api"},f="alert",d="fade",m="show",p=function(){function i(t){this._element=t}var t=i.prototype;return t.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},t.dispose=function(){g.removeData(this._element,r),this._element=null},t._getRootElement=function(t){var e=_.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=g(t).closest("."+f)[0]),n},t._triggerCloseEvent=function(t){var e=g.Event(h.CLOSE);return g(t).trigger(e),e},t._removeElement=function(e){var n=this;if(g(e).removeClass(m),g(e).hasClass(d)){var t=_.getTransitionDurationFromElement(e);g(e).one(_.TRANSITION_END,function(t){return n._destroyElement(e,t)}).emulateTransitionEnd(t)}else this._destroyElement(e)},t._destroyElement=function(t){g(t).detach().trigger(h.CLOSED).remove()},i._jQueryInterface=function(n){return this.each(function(){var t=g(this),e=t.data(r);e||(e=new i(this),t.data(r,e)),"close"===n&&e[n](this)})},i._handleDismiss=function(e){return function(t){t&&t.preventDefault(),e.close(this)}},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}}]),i}();g(document).on(h.CLICK_DATA_API,'[data-dismiss="alert"]',p._handleDismiss(new p)),g.fn[o]=p._jQueryInterface,g.fn[o].Constructor=p,g.fn[o].noConflict=function(){return g.fn[o]=c,p._jQueryInterface};var v="button",y="bs.button",E="."+y,C=".data-api",T=g.fn[v],S="active",b="btn",I="focus",D='[data-toggle^="button"]',w='[data-toggle="buttons"]',A='input:not([type="hidden"])',N=".active",O=".btn",k={CLICK_DATA_API:"click"+E+C,FOCUS_BLUR_DATA_API:"focus"+E+C+" blur"+E+C},P=function(){function n(t){this._element=t}var t=n.prototype;return t.toggle=function(){var t=!0,e=!0,n=g(this._element).closest(w)[0];if(n){var i=this._element.querySelector(A);if(i){if("radio"===i.type)if(i.checked&&this._element.classList.contains(S))t=!1;else{var o=n.querySelector(N);o&&g(o).removeClass(S)}if(t){if(i.hasAttribute("disabled")||n.hasAttribute("disabled")||i.classList.contains("disabled")||n.classList.contains("disabled"))return;i.checked=!this._element.classList.contains(S),g(i).trigger("change")}i.focus(),e=!1}}e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains(S)),t&&g(this._element).toggleClass(S)},t.dispose=function(){g.removeData(this._element,y),this._element=null},n._jQueryInterface=function(e){return this.each(function(){var t=g(this).data(y);t||(t=new n(this),g(this).data(y,t)),"toggle"===e&&t[e]()})},s(n,null,[{key:"VERSION",get:function(){return"4.3.1"}}]),n}();g(document).on(k.CLICK_DATA_API,D,function(t){t.preventDefault();var e=t.target;g(e).hasClass(b)||(e=g(e).closest(O)),P._jQueryInterface.call(g(e),"toggle")}).on(k.FOCUS_BLUR_DATA_API,D,function(t){var e=g(t.target).closest(O)[0];g(e).toggleClass(I,/^focus(in)?$/.test(t.type))}),g.fn[v]=P._jQueryInterface,g.fn[v].Constructor=P,g.fn[v].noConflict=function(){return g.fn[v]=T,P._jQueryInterface};var L="carousel",j="bs.carousel",H="."+j,R=".data-api",x=g.fn[L],F={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},U={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},W="next",q="prev",M="left",K="right",Q={SLIDE:"slide"+H,SLID:"slid"+H,KEYDOWN:"keydown"+H,MOUSEENTER:"mouseenter"+H,MOUSELEAVE:"mouseleave"+H,TOUCHSTART:"touchstart"+H,TOUCHMOVE:"touchmove"+H,TOUCHEND:"touchend"+H,POINTERDOWN:"pointerdown"+H,POINTERUP:"pointerup"+H,DRAG_START:"dragstart"+H,LOAD_DATA_API:"load"+H+R,CLICK_DATA_API:"click"+H+R},B="carousel",V="active",Y="slide",z="carousel-item-right",X="carousel-item-left",$="carousel-item-next",G="carousel-item-prev",J="pointer-event",Z=".active",tt=".active.carousel-item",et=".carousel-item",nt=".carousel-item img",it=".carousel-item-next, .carousel-item-prev",ot=".carousel-indicators",rt="[data-slide], [data-slide-to]",st='[data-ride="carousel"]',at={TOUCH:"touch",PEN:"pen"},lt=function(){function r(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(ot),this._touchSupported="ontouchstart"in document.documentElement||0<navigator.maxTouchPoints,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var t=r.prototype;return t.next=function(){this._isSliding||this._slide(W)},t.nextWhenVisible=function(){!document.hidden&&g(this._element).is(":visible")&&"hidden"!==g(this._element).css("visibility")&&this.next()},t.prev=function(){this._isSliding||this._slide(q)},t.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(it)&&(_.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},t.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},t.to=function(t){var e=this;this._activeElement=this._element.querySelector(tt);var n=this._getItemIndex(this._activeElement);if(!(t>this._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=n<t?W:q;this._slide(i,this._items[t])}},t.dispose=function(){g(this._element).off(H),g.removeData(this._element,j),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},t._getConfig=function(t){return t=l({},F,t),_.typeCheckConfig(L,t,U),t},t._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;0<e&&this.prev(),e<0&&this.next()}},t._addEventListeners=function(){var e=this;this._config.keyboard&&g(this._element).on(Q.KEYDOWN,function(t){return e._keydown(t)}),"hover"===this._config.pause&&g(this._element).on(Q.MOUSEENTER,function(t){return e.pause(t)}).on(Q.MOUSELEAVE,function(t){return e.cycle(t)}),this._config.touch&&this._addTouchEventListeners()},t._addTouchEventListeners=function(){var n=this;if(this._touchSupported){var e=function(t){n._pointerEvent&&at[t.originalEvent.pointerType.toUpperCase()]?n.touchStartX=t.originalEvent.clientX:n._pointerEvent||(n.touchStartX=t.originalEvent.touches[0].clientX)},i=function(t){n._pointerEvent&&at[t.originalEvent.pointerType.toUpperCase()]&&(n.touchDeltaX=t.originalEvent.clientX-n.touchStartX),n._handleSwipe(),"hover"===n._config.pause&&(n.pause(),n.touchTimeout&&clearTimeout(n.touchTimeout),n.touchTimeout=setTimeout(function(t){return n.cycle(t)},500+n._config.interval))};g(this._element.querySelectorAll(nt)).on(Q.DRAG_START,function(t){return t.preventDefault()}),this._pointerEvent?(g(this._element).on(Q.POINTERDOWN,function(t){return e(t)}),g(this._element).on(Q.POINTERUP,function(t){return i(t)}),this._element.classList.add(J)):(g(this._element).on(Q.TOUCHSTART,function(t){return e(t)}),g(this._element).on(Q.TOUCHMOVE,function(t){var e;(e=t).originalEvent.touches&&1<e.originalEvent.touches.length?n.touchDeltaX=0:n.touchDeltaX=e.originalEvent.touches[0].clientX-n.touchStartX}),g(this._element).on(Q.TOUCHEND,function(t){return i(t)}))}},t._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},t._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(et)):[],this._items.indexOf(t)},t._getItemByDirection=function(t,e){var n=t===W,i=t===q,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var s=(o+(t===q?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},t._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),i=this._getItemIndex(this._element.querySelector(tt)),o=g.Event(Q.SLIDE,{relatedTarget:t,direction:e,from:i,to:n});return g(this._element).trigger(o),o},t._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(Z));g(e).removeClass(V);var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&g(n).addClass(V)}},t._slide=function(t,e){var n,i,o,r=this,s=this._element.querySelector(tt),a=this._getItemIndex(s),l=e||s&&this._getItemByDirection(t,s),c=this._getItemIndex(l),h=Boolean(this._interval);if(o=t===W?(n=X,i=$,M):(n=z,i=G,K),l&&g(l).hasClass(V))this._isSliding=!1;else if(!this._triggerSlideEvent(l,o).isDefaultPrevented()&&s&&l){this._isSliding=!0,h&&this.pause(),this._setActiveIndicatorElement(l);var u=g.Event(Q.SLID,{relatedTarget:l,direction:o,from:a,to:c});if(g(this._element).hasClass(Y)){g(l).addClass(i),_.reflow(l),g(s).addClass(n),g(l).addClass(n);var f=parseInt(l.getAttribute("data-interval"),10);this._config.interval=f?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,f):this._config.defaultInterval||this._config.interval;var d=_.getTransitionDurationFromElement(s);g(s).one(_.TRANSITION_END,function(){g(l).removeClass(n+" "+i).addClass(V),g(s).removeClass(V+" "+i+" "+n),r._isSliding=!1,setTimeout(function(){return g(r._element).trigger(u)},0)}).emulateTransitionEnd(d)}else g(s).removeClass(V),g(l).addClass(V),this._isSliding=!1,g(this._element).trigger(u);h&&this.cycle()}},r._jQueryInterface=function(i){return this.each(function(){var t=g(this).data(j),e=l({},F,g(this).data());"object"==typeof i&&(e=l({},e,i));var n="string"==typeof i?i:e.slide;if(t||(t=new r(this,e),g(this).data(j,t)),"number"==typeof i)t.to(i);else if("string"==typeof n){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}else e.interval&&e.ride&&(t.pause(),t.cycle())})},r._dataApiClickHandler=function(t){var e=_.getSelectorFromElement(this);if(e){var n=g(e)[0];if(n&&g(n).hasClass(B)){var i=l({},g(n).data(),g(this).data()),o=this.getAttribute("data-slide-to");o&&(i.interval=!1),r._jQueryInterface.call(g(n),i),o&&g(n).data(j).to(o),t.preventDefault()}}},s(r,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return F}}]),r}();g(document).on(Q.CLICK_DATA_API,rt,lt._dataApiClickHandler),g(window).on(Q.LOAD_DATA_API,function(){for(var t=[].slice.call(document.querySelectorAll(st)),e=0,n=t.length;e<n;e++){var i=g(t[e]);lt._jQueryInterface.call(i,i.data())}}),g.fn[L]=lt._jQueryInterface,g.fn[L].Constructor=lt,g.fn[L].noConflict=function(){return g.fn[L]=x,lt._jQueryInterface};var ct="collapse",ht="bs.collapse",ut="."+ht,ft=g.fn[ct],dt={toggle:!0,parent:""},gt={toggle:"boolean",parent:"(string|element)"},_t={SHOW:"show"+ut,SHOWN:"shown"+ut,HIDE:"hide"+ut,HIDDEN:"hidden"+ut,CLICK_DATA_API:"click"+ut+".data-api"},mt="show",pt="collapse",vt="collapsing",yt="collapsed",Et="width",Ct="height",Tt=".show, .collapsing",St='[data-toggle="collapse"]',bt=function(){function a(e,t){this._isTransitioning=!1,this._element=e,this._config=this._getConfig(t),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+e.id+'"],[data-toggle="collapse"][data-target="#'+e.id+'"]'));for(var n=[].slice.call(document.querySelectorAll(St)),i=0,o=n.length;i<o;i++){var r=n[i],s=_.getSelectorFromElement(r),a=[].slice.call(document.querySelectorAll(s)).filter(function(t){return t===e});null!==s&&0<a.length&&(this._selector=s,this._triggerArray.push(r))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var t=a.prototype;return t.toggle=function(){g(this._element).hasClass(mt)?this.hide():this.show()},t.show=function(){var t,e,n=this;if(!this._isTransitioning&&!g(this._element).hasClass(mt)&&(this._parent&&0===(t=[].slice.call(this._parent.querySelectorAll(Tt)).filter(function(t){return"string"==typeof n._config.parent?t.getAttribute("data-parent")===n._config.parent:t.classList.contains(pt)})).length&&(t=null),!(t&&(e=g(t).not(this._selector).data(ht))&&e._isTransitioning))){var i=g.Event(_t.SHOW);if(g(this._element).trigger(i),!i.isDefaultPrevented()){t&&(a._jQueryInterface.call(g(t).not(this._selector),"hide"),e||g(t).data(ht,null));var o=this._getDimension();g(this._element).removeClass(pt).addClass(vt),this._element.style[o]=0,this._triggerArray.length&&g(this._triggerArray).removeClass(yt).attr("aria-expanded",!0),this.setTransitioning(!0);var r="scroll"+(o[0].toUpperCase()+o.slice(1)),s=_.getTransitionDurationFromElement(this._element);g(this._element).one(_.TRANSITION_END,function(){g(n._element).removeClass(vt).addClass(pt).addClass(mt),n._element.style[o]="",n.setTransitioning(!1),g(n._element).trigger(_t.SHOWN)}).emulateTransitionEnd(s),this._element.style[o]=this._element[r]+"px"}}},t.hide=function(){var t=this;if(!this._isTransitioning&&g(this._element).hasClass(mt)){var e=g.Event(_t.HIDE);if(g(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",_.reflow(this._element),g(this._element).addClass(vt).removeClass(pt).removeClass(mt);var i=this._triggerArray.length;if(0<i)for(var o=0;o<i;o++){var r=this._triggerArray[o],s=_.getSelectorFromElement(r);if(null!==s)g([].slice.call(document.querySelectorAll(s))).hasClass(mt)||g(r).addClass(yt).attr("aria-expanded",!1)}this.setTransitioning(!0);this._element.style[n]="";var a=_.getTransitionDurationFromElement(this._element);g(this._element).one(_.TRANSITION_END,function(){t.setTransitioning(!1),g(t._element).removeClass(vt).addClass(pt).trigger(_t.HIDDEN)}).emulateTransitionEnd(a)}}},t.setTransitioning=function(t){this._isTransitioning=t},t.dispose=function(){g.removeData(this._element,ht),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},t._getConfig=function(t){return(t=l({},dt,t)).toggle=Boolean(t.toggle),_.typeCheckConfig(ct,t,gt),t},t._getDimension=function(){return g(this._element).hasClass(Et)?Et:Ct},t._getParent=function(){var t,n=this;_.isElement(this._config.parent)?(t=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(t=this._config.parent[0])):t=document.querySelector(this._config.parent);var e='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',i=[].slice.call(t.querySelectorAll(e));return g(i).each(function(t,e){n._addAriaAndCollapsedClass(a._getTargetFromElement(e),[e])}),t},t._addAriaAndCollapsedClass=function(t,e){var n=g(t).hasClass(mt);e.length&&g(e).toggleClass(yt,!n).attr("aria-expanded",n)},a._getTargetFromElement=function(t){var e=_.getSelectorFromElement(t);return e?document.querySelector(e):null},a._jQueryInterface=function(i){return this.each(function(){var t=g(this),e=t.data(ht),n=l({},dt,t.data(),"object"==typeof i&&i?i:{});if(!e&&n.toggle&&/show|hide/.test(i)&&(n.toggle=!1),e||(e=new a(this,n),t.data(ht,e)),"string"==typeof i){if("undefined"==typeof e[i])throw new TypeError('No method named "'+i+'"');e[i]()}})},s(a,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return dt}}]),a}();g(document).on(_t.CLICK_DATA_API,St,function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var n=g(this),e=_.getSelectorFromElement(this),i=[].slice.call(document.querySelectorAll(e));g(i).each(function(){var t=g(this),e=t.data(ht)?"toggle":n.data();bt._jQueryInterface.call(t,e)})}),g.fn[ct]=bt._jQueryInterface,g.fn[ct].Constructor=bt,g.fn[ct].noConflict=function(){return g.fn[ct]=ft,bt._jQueryInterface};var It="dropdown",Dt="bs.dropdown",wt="."+Dt,At=".data-api",Nt=g.fn[It],Ot=new RegExp("38|40|27"),kt={HIDE:"hide"+wt,HIDDEN:"hidden"+wt,SHOW:"show"+wt,SHOWN:"shown"+wt,CLICK:"click"+wt,CLICK_DATA_API:"click"+wt+At,KEYDOWN_DATA_API:"keydown"+wt+At,KEYUP_DATA_API:"keyup"+wt+At},Pt="disabled",Lt="show",jt="dropup",Ht="dropright",Rt="dropleft",xt="dropdown-menu-right",Ft="position-static",Ut='[data-toggle="dropdown"]',Wt=".dropdown form",qt=".dropdown-menu",Mt=".navbar-nav",Kt=".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",Qt="top-start",Bt="top-end",Vt="bottom-start",Yt="bottom-end",zt="right-start",Xt="left-start",$t={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic"},Gt={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string"},Jt=function(){function c(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var t=c.prototype;return t.toggle=function(){if(!this._element.disabled&&!g(this._element).hasClass(Pt)){var t=c._getParentFromElement(this._element),e=g(this._menu).hasClass(Lt);if(c._clearMenus(),!e){var n={relatedTarget:this._element},i=g.Event(kt.SHOW,n);if(g(t).trigger(i),!i.isDefaultPrevented()){if(!this._inNavbar){if("undefined"==typeof u)throw new TypeError("Bootstrap's dropdowns require Popper.js (https://popper.js.org/)");var o=this._element;"parent"===this._config.reference?o=t:_.isElement(this._config.reference)&&(o=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(o=this._config.reference[0])),"scrollParent"!==this._config.boundary&&g(t).addClass(Ft),this._popper=new u(o,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===g(t).closest(Mt).length&&g(document.body).children().on("mouseover",null,g.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),g(this._menu).toggleClass(Lt),g(t).toggleClass(Lt).trigger(g.Event(kt.SHOWN,n))}}}},t.show=function(){if(!(this._element.disabled||g(this._element).hasClass(Pt)||g(this._menu).hasClass(Lt))){var t={relatedTarget:this._element},e=g.Event(kt.SHOW,t),n=c._getParentFromElement(this._element);g(n).trigger(e),e.isDefaultPrevented()||(g(this._menu).toggleClass(Lt),g(n).toggleClass(Lt).trigger(g.Event(kt.SHOWN,t)))}},t.hide=function(){if(!this._element.disabled&&!g(this._element).hasClass(Pt)&&g(this._menu).hasClass(Lt)){var t={relatedTarget:this._element},e=g.Event(kt.HIDE,t),n=c._getParentFromElement(this._element);g(n).trigger(e),e.isDefaultPrevented()||(g(this._menu).toggleClass(Lt),g(n).toggleClass(Lt).trigger(g.Event(kt.HIDDEN,t)))}},t.dispose=function(){g.removeData(this._element,Dt),g(this._element).off(wt),this._element=null,(this._menu=null)!==this._popper&&(this._popper.destroy(),this._popper=null)},t.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},t._addEventListeners=function(){var e=this;g(this._element).on(kt.CLICK,function(t){t.preventDefault(),t.stopPropagation(),e.toggle()})},t._getConfig=function(t){return t=l({},this.constructor.Default,g(this._element).data(),t),_.typeCheckConfig(It,t,this.constructor.DefaultType),t},t._getMenuElement=function(){if(!this._menu){var t=c._getParentFromElement(this._element);t&&(this._menu=t.querySelector(qt))}return this._menu},t._getPlacement=function(){var t=g(this._element.parentNode),e=Vt;return t.hasClass(jt)?(e=Qt,g(this._menu).hasClass(xt)&&(e=Bt)):t.hasClass(Ht)?e=zt:t.hasClass(Rt)?e=Xt:g(this._menu).hasClass(xt)&&(e=Yt),e},t._detectNavbar=function(){return 0<g(this._element).closest(".navbar").length},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._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),t},c._jQueryInterface=function(e){return this.each(function(){var t=g(this).data(Dt);if(t||(t=new c(this,"object"==typeof e?e:null),g(this).data(Dt,t)),"string"==typeof e){if("undefined"==typeof t[e])throw new TypeError('No method named "'+e+'"');t[e]()}})},c._clearMenus=function(t){if(!t||3!==t.which&&("keyup"!==t.type||9===t.which))for(var e=[].slice.call(document.querySelectorAll(Ut)),n=0,i=e.length;n<i;n++){var o=c._getParentFromElement(e[n]),r=g(e[n]).data(Dt),s={relatedTarget:e[n]};if(t&&"click"===t.type&&(s.clickEvent=t),r){var a=r._menu;if(g(o).hasClass(Lt)&&!(t&&("click"===t.type&&/input|textarea/i.test(t.target.tagName)||"keyup"===t.type&&9===t.which)&&g.contains(o,t.target))){var l=g.Event(kt.HIDE,s);g(o).trigger(l),l.isDefaultPrevented()||("ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),e[n].setAttribute("aria-expanded","false"),g(a).removeClass(Lt),g(o).removeClass(Lt).trigger(g.Event(kt.HIDDEN,s)))}}}},c._getParentFromElement=function(t){var e,n=_.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},c._dataApiKeydownHandler=function(t){if((/input|textarea/i.test(t.target.tagName)?!(32===t.which||27!==t.which&&(40!==t.which&&38!==t.which||g(t.target).closest(qt).length)):Ot.test(t.which))&&(t.preventDefault(),t.stopPropagation(),!this.disabled&&!g(this).hasClass(Pt))){var e=c._getParentFromElement(this),n=g(e).hasClass(Lt);if(n&&(!n||27!==t.which&&32!==t.which)){var i=[].slice.call(e.querySelectorAll(Kt));if(0!==i.length){var o=i.indexOf(t.target);38===t.which&&0<o&&o--,40===t.which&&o<i.length-1&&o++,o<0&&(o=0),i[o].focus()}}else{if(27===t.which){var r=e.querySelector(Ut);g(r).trigger("focus")}g(this).trigger("click")}}},s(c,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return $t}},{key:"DefaultType",get:function(){return Gt}}]),c}();g(document).on(kt.KEYDOWN_DATA_API,Ut,Jt._dataApiKeydownHandler).on(kt.KEYDOWN_DATA_API,qt,Jt._dataApiKeydownHandler).on(kt.CLICK_DATA_API+" "+kt.KEYUP_DATA_API,Jt._clearMenus).on(kt.CLICK_DATA_API,Ut,function(t){t.preventDefault(),t.stopPropagation(),Jt._jQueryInterface.call(g(this),"toggle")}).on(kt.CLICK_DATA_API,Wt,function(t){t.stopPropagation()}),g.fn[It]=Jt._jQueryInterface,g.fn[It].Constructor=Jt,g.fn[It].noConflict=function(){return g.fn[It]=Nt,Jt._jQueryInterface};var Zt="modal",te="bs.modal",ee="."+te,ne=g.fn[Zt],ie={backdrop:!0,keyboard:!0,focus:!0,show:!0},oe={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},re={HIDE:"hide"+ee,HIDDEN:"hidden"+ee,SHOW:"show"+ee,SHOWN:"shown"+ee,FOCUSIN:"focusin"+ee,RESIZE:"resize"+ee,CLICK_DISMISS:"click.dismiss"+ee,KEYDOWN_DISMISS:"keydown.dismiss"+ee,MOUSEUP_DISMISS:"mouseup.dismiss"+ee,MOUSEDOWN_DISMISS:"mousedown.dismiss"+ee,CLICK_DATA_API:"click"+ee+".data-api"},se="modal-dialog-scrollable",ae="modal-scrollbar-measure",le="modal-backdrop",ce="modal-open",he="fade",ue="show",fe=".modal-dialog",de=".modal-body",ge='[data-toggle="modal"]',_e='[data-dismiss="modal"]',me=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",pe=".sticky-top",ve=function(){function o(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(fe),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var t=o.prototype;return t.toggle=function(t){return this._isShown?this.hide():this.show(t)},t.show=function(t){var e=this;if(!this._isShown&&!this._isTransitioning){g(this._element).hasClass(he)&&(this._isTransitioning=!0);var n=g.Event(re.SHOW,{relatedTarget:t});g(this._element).trigger(n),this._isShown||n.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),g(this._element).on(re.CLICK_DISMISS,_e,function(t){return e.hide(t)}),g(this._dialog).on(re.MOUSEDOWN_DISMISS,function(){g(e._element).one(re.MOUSEUP_DISMISS,function(t){g(t.target).is(e._element)&&(e._ignoreBackdropClick=!0)})}),this._showBackdrop(function(){return e._showElement(t)}))}},t.hide=function(t){var e=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var n=g.Event(re.HIDE);if(g(this._element).trigger(n),this._isShown&&!n.isDefaultPrevented()){this._isShown=!1;var i=g(this._element).hasClass(he);if(i&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),g(document).off(re.FOCUSIN),g(this._element).removeClass(ue),g(this._element).off(re.CLICK_DISMISS),g(this._dialog).off(re.MOUSEDOWN_DISMISS),i){var o=_.getTransitionDurationFromElement(this._element);g(this._element).one(_.TRANSITION_END,function(t){return e._hideModal(t)}).emulateTransitionEnd(o)}else this._hideModal()}}},t.dispose=function(){[window,this._element,this._dialog].forEach(function(t){return g(t).off(ee)}),g(document).off(re.FOCUSIN),g.removeData(this._element,te),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},t.handleUpdate=function(){this._adjustDialog()},t._getConfig=function(t){return t=l({},ie,t),_.typeCheckConfig(Zt,t,oe),t},t._showElement=function(t){var e=this,n=g(this._element).hasClass(he);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),g(this._dialog).hasClass(se)?this._dialog.querySelector(de).scrollTop=0:this._element.scrollTop=0,n&&_.reflow(this._element),g(this._element).addClass(ue),this._config.focus&&this._enforceFocus();var i=g.Event(re.SHOWN,{relatedTarget:t}),o=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,g(e._element).trigger(i)};if(n){var r=_.getTransitionDurationFromElement(this._dialog);g(this._dialog).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o()},t._enforceFocus=function(){var e=this;g(document).off(re.FOCUSIN).on(re.FOCUSIN,function(t){document!==t.target&&e._element!==t.target&&0===g(e._element).has(t.target).length&&e._element.focus()})},t._setEscapeEvent=function(){var e=this;this._isShown&&this._config.keyboard?g(this._element).on(re.KEYDOWN_DISMISS,function(t){27===t.which&&(t.preventDefault(),e.hide())}):this._isShown||g(this._element).off(re.KEYDOWN_DISMISS)},t._setResizeEvent=function(){var e=this;this._isShown?g(window).on(re.RESIZE,function(t){return e.handleUpdate(t)}):g(window).off(re.RESIZE)},t._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._isTransitioning=!1,this._showBackdrop(function(){g(document.body).removeClass(ce),t._resetAdjustments(),t._resetScrollbar(),g(t._element).trigger(re.HIDDEN)})},t._removeBackdrop=function(){this._backdrop&&(g(this._backdrop).remove(),this._backdrop=null)},t._showBackdrop=function(t){var e=this,n=g(this._element).hasClass(he)?he:"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className=le,n&&this._backdrop.classList.add(n),g(this._backdrop).appendTo(document.body),g(this._element).on(re.CLICK_DISMISS,function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"===e._config.backdrop?e._element.focus():e.hide())}),n&&_.reflow(this._backdrop),g(this._backdrop).addClass(ue),!t)return;if(!n)return void t();var i=_.getTransitionDurationFromElement(this._backdrop);g(this._backdrop).one(_.TRANSITION_END,t).emulateTransitionEnd(i)}else if(!this._isShown&&this._backdrop){g(this._backdrop).removeClass(ue);var o=function(){e._removeBackdrop(),t&&t()};if(g(this._element).hasClass(he)){var r=_.getTransitionDurationFromElement(this._backdrop);g(this._backdrop).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o()}else t&&t()},t._adjustDialog=function(){var t=this._element.scrollHeight>document.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<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},t._setScrollbar=function(){var o=this;if(this._isBodyOverflowing){var t=[].slice.call(document.querySelectorAll(me)),e=[].slice.call(document.querySelectorAll(pe));g(t).each(function(t,e){var n=e.style.paddingRight,i=g(e).css("padding-right");g(e).data("padding-right",n).css("padding-right",parseFloat(i)+o._scrollbarWidth+"px")}),g(e).each(function(t,e){var n=e.style.marginRight,i=g(e).css("margin-right");g(e).data("margin-right",n).css("margin-right",parseFloat(i)-o._scrollbarWidth+"px")});var n=document.body.style.paddingRight,i=g(document.body).css("padding-right");g(document.body).data("padding-right",n).css("padding-right",parseFloat(i)+this._scrollbarWidth+"px")}g(document.body).addClass(ce)},t._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(me));g(t).each(function(t,e){var n=g(e).data("padding-right");g(e).removeData("padding-right"),e.style.paddingRight=n||""});var e=[].slice.call(document.querySelectorAll(""+pe));g(e).each(function(t,e){var n=g(e).data("margin-right");"undefined"!=typeof n&&g(e).css("margin-right",n).removeData("margin-right")});var n=g(document.body).data("padding-right");g(document.body).removeData("padding-right"),document.body.style.paddingRight=n||""},t._getScrollbarWidth=function(){var t=document.createElement("div");t.className=ae,document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},o._jQueryInterface=function(n,i){return this.each(function(){var t=g(this).data(te),e=l({},ie,g(this).data(),"object"==typeof n&&n?n:{});if(t||(t=new o(this,e),g(this).data(te,t)),"string"==typeof n){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n](i)}else e.show&&t.show(i)})},s(o,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return ie}}]),o}();g(document).on(re.CLICK_DATA_API,ge,function(t){var e,n=this,i=_.getSelectorFromElement(this);i&&(e=document.querySelector(i));var o=g(e).data(te)?"toggle":l({},g(e).data(),g(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var r=g(e).one(re.SHOW,function(t){t.isDefaultPrevented()||r.one(re.HIDDEN,function(){g(n).is(":visible")&&n.focus()})});ve._jQueryInterface.call(g(e),o,this)}),g.fn[Zt]=ve._jQueryInterface,g.fn[Zt].Constructor=ve,g.fn[Zt].noConflict=function(){return g.fn[Zt]=ne,ve._jQueryInterface};var ye=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],Ee={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Ce=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,Te=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function Se(t,s,e){if(0===t.length)return t;if(e&&"function"==typeof e)return e(t);for(var n=(new window.DOMParser).parseFromString(t,"text/html"),a=Object.keys(s),l=[].slice.call(n.body.querySelectorAll("*")),i=function(t,e){var n=l[t],i=n.nodeName.toLowerCase();if(-1===a.indexOf(n.nodeName.toLowerCase()))return n.parentNode.removeChild(n),"continue";var o=[].slice.call(n.attributes),r=[].concat(s["*"]||[],s[i]||[]);o.forEach(function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===ye.indexOf(n)||Boolean(t.nodeValue.match(Ce)||t.nodeValue.match(Te));for(var i=e.filter(function(t){return t instanceof RegExp}),o=0,r=i.length;o<r;o++)if(n.match(i[o]))return!0;return!1})(t,r)||n.removeAttribute(t.nodeName)})},o=0,r=l.length;o<r;o++)i(o);return n.body.innerHTML}var be="tooltip",Ie="bs.tooltip",De="."+Ie,we=g.fn[be],Ae="bs-tooltip",Ne=new RegExp("(^|\\s)"+Ae+"\\S+","g"),Oe=["sanitize","whiteList","sanitizeFn"],ke={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object"},Pe={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},Le={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Ee},je="show",He="out",Re={HIDE:"hide"+De,HIDDEN:"hidden"+De,SHOW:"show"+De,SHOWN:"shown"+De,INSERTED:"inserted"+De,CLICK:"click"+De,FOCUSIN:"focusin"+De,FOCUSOUT:"focusout"+De,MOUSEENTER:"mouseenter"+De,MOUSELEAVE:"mouseleave"+De},xe="fade",Fe="show",Ue=".tooltip-inner",We=".arrow",qe="hover",Me="focus",Ke="click",Qe="manual",Be=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(Fe))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(xe);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:We},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(Fe),"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===He&&e._leave(null,e)};if(g(this.tip).hasClass(xe)){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!==je&&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(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ke]=!1,this._activeTrigger[Me]=!1,this._activeTrigger[qe]=!1,g(this.tip).hasClass(xe)){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(Ae+"-"+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(Ue)),this.getTitle()),g(t).removeClass(xe+" "+Fe)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Se(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},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 Pe[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!==Qe){var e=t===qe?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===qe?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?Me:qe]=!0),g(e.getTipElement()).hasClass(Fe)||e._hoverState===je?e._hoverState=je:(clearTimeout(e._timeout),e._hoverState=je,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===je&&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?Me:qe]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=He,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===He&&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){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==Oe.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,e,"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(be,t,this.constructor.DefaultType),t.sanitize&&(t.template=Se(t.template,t.whiteList,t.sanitizeFn)),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(Ne);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(xe),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(Ie),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ie,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.1"}},{key:"Default",get:function(){return Le}},{key:"NAME",get:function(){return be}},{key:"DATA_KEY",get:function(){return Ie}},{key:"Event",get:function(){return Re}},{key:"EVENT_KEY",get:function(){return De}},{key:"DefaultType",get:function(){return ke}}]),i}();g.fn[be]=Be._jQueryInterface,g.fn[be].Constructor=Be,g.fn[be].noConflict=function(){return g.fn[be]=we,Be._jQueryInterface};var Ve="popover",Ye="bs.popover",ze="."+Ye,Xe=g.fn[Ve],$e="bs-popover",Ge=new RegExp("(^|\\s)"+$e+"\\S+","g"),Je=l({},Be.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),Ze=l({},Be.DefaultType,{content:"(string|element|function)"}),tn="fade",en="show",nn=".popover-header",on=".popover-body",rn={HIDE:"hide"+ze,HIDDEN:"hidden"+ze,SHOW:"show"+ze,SHOWN:"shown"+ze,INSERTED:"inserted"+ze,CLICK:"click"+ze,FOCUSIN:"focusin"+ze,FOCUSOUT:"focusout"+ze,MOUSEENTER:"mouseenter"+ze,MOUSELEAVE:"mouseleave"+ze},sn=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($e+"-"+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(nn),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(on),e),t.removeClass(tn+" "+en)},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(Ge);null!==e&&0<e.length&&t.removeClass(e.join(""))},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ye),e="object"==typeof n?n:null;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ye,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.1"}},{key:"Default",get:function(){return Je}},{key:"NAME",get:function(){return Ve}},{key:"DATA_KEY",get:function(){return Ye}},{key:"Event",get:function(){return rn}},{key:"EVENT_KEY",get:function(){return ze}},{key:"DefaultType",get:function(){return Ze}}]),i}(Be);g.fn[Ve]=sn._jQueryInterface,g.fn[Ve].Constructor=sn,g.fn[Ve].noConflict=function(){return g.fn[Ve]=Xe,sn._jQueryInterface};var an="scrollspy",ln="bs.scrollspy",cn="."+ln,hn=g.fn[an],un={offset:10,method:"auto",target:""},fn={offset:"number",method:"string",target:"(string|element)"},dn={ACTIVATE:"activate"+cn,SCROLL:"scroll"+cn,LOAD_DATA_API:"load"+cn+".data-api"},gn="dropdown-item",_n="active",mn='[data-spy="scroll"]',pn=".nav, .list-group",vn=".nav-link",yn=".nav-item",En=".list-group-item",Cn=".dropdown",Tn=".dropdown-item",Sn=".dropdown-toggle",bn="offset",In="position",Dn=function(){function n(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" "+vn+","+this._config.target+" "+En+","+this._config.target+" "+Tn,this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,g(this._scrollElement).on(dn.SCROLL,function(t){return n._process(t)}),this.refresh(),this._process()}var t=n.prototype;return t.refresh=function(){var e=this,t=this._scrollElement===this._scrollElement.window?bn:In,o="auto"===this._config.method?t:this._config.method,r=o===In?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map(function(t){var e,n=_.getSelectorFromElement(t);if(n&&(e=document.querySelector(n)),e){var i=e.getBoundingClientRect();if(i.width||i.height)return[g(e)[o]().top+r,n]}return null}).filter(function(t){return t}).sort(function(t,e){return t[0]-e[0]}).forEach(function(t){e._offsets.push(t[0]),e._targets.push(t[1])})},t.dispose=function(){g.removeData(this._element,ln),g(this._scrollElement).off(cn),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},t._getConfig=function(t){if("string"!=typeof(t=l({},un,"object"==typeof t&&t?t:{})).target){var e=g(t.target).attr("id");e||(e=_.getUID(an),g(t.target).attr("id",e)),t.target="#"+e}return _.typeCheckConfig(an,t,fn),t},t._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},t._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},t._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},t._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),n<=t){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&0<this._offsets[0])return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}}},t._activate=function(e){this._activeTarget=e,this._clear();var t=this._selector.split(",").map(function(t){return t+'[data-target="'+e+'"],'+t+'[href="'+e+'"]'}),n=g([].slice.call(document.querySelectorAll(t.join(","))));n.hasClass(gn)?(n.closest(Cn).find(Sn).addClass(_n),n.addClass(_n)):(n.addClass(_n),n.parents(pn).prev(vn+", "+En).addClass(_n),n.parents(pn).prev(yn).children(vn).addClass(_n)),g(this._scrollElement).trigger(dn.ACTIVATE,{relatedTarget:e})},t._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter(function(t){return t.classList.contains(_n)}).forEach(function(t){return t.classList.remove(_n)})},n._jQueryInterface=function(e){return this.each(function(){var t=g(this).data(ln);if(t||(t=new n(this,"object"==typeof e&&e),g(this).data(ln,t)),"string"==typeof e){if("undefined"==typeof t[e])throw new TypeError('No method named "'+e+'"');t[e]()}})},s(n,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return un}}]),n}();g(window).on(dn.LOAD_DATA_API,function(){for(var t=[].slice.call(document.querySelectorAll(mn)),e=t.length;e--;){var n=g(t[e]);Dn._jQueryInterface.call(n,n.data())}}),g.fn[an]=Dn._jQueryInterface,g.fn[an].Constructor=Dn,g.fn[an].noConflict=function(){return g.fn[an]=hn,Dn._jQueryInterface};var wn="bs.tab",An="."+wn,Nn=g.fn.tab,On={HIDE:"hide"+An,HIDDEN:"hidden"+An,SHOW:"show"+An,SHOWN:"shown"+An,CLICK_DATA_API:"click"+An+".data-api"},kn="dropdown-menu",Pn="active",Ln="disabled",jn="fade",Hn="show",Rn=".dropdown",xn=".nav, .list-group",Fn=".active",Un="> li > .active",Wn='[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',qn=".dropdown-toggle",Mn="> .dropdown-menu .active",Kn=function(){function i(t){this._element=t}var t=i.prototype;return t.show=function(){var n=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&g(this._element).hasClass(Pn)||g(this._element).hasClass(Ln))){var t,i,e=g(this._element).closest(xn)[0],o=_.getSelectorFromElement(this._element);if(e){var r="UL"===e.nodeName||"OL"===e.nodeName?Un:Fn;i=(i=g.makeArray(g(e).find(r)))[i.length-1]}var s=g.Event(On.HIDE,{relatedTarget:this._element}),a=g.Event(On.SHOW,{relatedTarget:i});if(i&&g(i).trigger(s),g(this._element).trigger(a),!a.isDefaultPrevented()&&!s.isDefaultPrevented()){o&&(t=document.querySelector(o)),this._activate(this._element,e);var l=function(){var t=g.Event(On.HIDDEN,{relatedTarget:n._element}),e=g.Event(On.SHOWN,{relatedTarget:i});g(i).trigger(t),g(n._element).trigger(e)};t?this._activate(t,t.parentNode,l):l()}}},t.dispose=function(){g.removeData(this._element,wn),this._element=null},t._activate=function(t,e,n){var i=this,o=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?g(e).children(Fn):g(e).find(Un))[0],r=n&&o&&g(o).hasClass(jn),s=function(){return i._transitionComplete(t,o,n)};if(o&&r){var a=_.getTransitionDurationFromElement(o);g(o).removeClass(Hn).one(_.TRANSITION_END,s).emulateTransitionEnd(a)}else s()},t._transitionComplete=function(t,e,n){if(e){g(e).removeClass(Pn);var i=g(e.parentNode).find(Mn)[0];i&&g(i).removeClass(Pn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}if(g(t).addClass(Pn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),_.reflow(t),t.classList.contains(jn)&&t.classList.add(Hn),t.parentNode&&g(t.parentNode).hasClass(kn)){var o=g(t).closest(Rn)[0];if(o){var r=[].slice.call(o.querySelectorAll(qn));g(r).addClass(Pn)}t.setAttribute("aria-expanded",!0)}n&&n()},i._jQueryInterface=function(n){return this.each(function(){var t=g(this),e=t.data(wn);if(e||(e=new i(this),t.data(wn,e)),"string"==typeof n){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}}]),i}();g(document).on(On.CLICK_DATA_API,Wn,function(t){t.preventDefault(),Kn._jQueryInterface.call(g(this),"show")}),g.fn.tab=Kn._jQueryInterface,g.fn.tab.Constructor=Kn,g.fn.tab.noConflict=function(){return g.fn.tab=Nn,Kn._jQueryInterface};var Qn="toast",Bn="bs.toast",Vn="."+Bn,Yn=g.fn[Qn],zn={CLICK_DISMISS:"click.dismiss"+Vn,HIDE:"hide"+Vn,HIDDEN:"hidden"+Vn,SHOW:"show"+Vn,SHOWN:"shown"+Vn},Xn="fade",$n="hide",Gn="show",Jn="showing",Zn={animation:"boolean",autohide:"boolean",delay:"number"},ti={animation:!0,autohide:!0,delay:500},ei='[data-dismiss="toast"]',ni=function(){function i(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var t=i.prototype;return t.show=function(){var t=this;g(this._element).trigger(zn.SHOW),this._config.animation&&this._element.classList.add(Xn);var e=function(){t._element.classList.remove(Jn),t._element.classList.add(Gn),g(t._element).trigger(zn.SHOWN),t._config.autohide&&t.hide()};if(this._element.classList.remove($n),this._element.classList.add(Jn),this._config.animation){var n=_.getTransitionDurationFromElement(this._element);g(this._element).one(_.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},t.hide=function(t){var e=this;this._element.classList.contains(Gn)&&(g(this._element).trigger(zn.HIDE),t?this._close():this._timeout=setTimeout(function(){e._close()},this._config.delay))},t.dispose=function(){clearTimeout(this._timeout),this._timeout=null,this._element.classList.contains(Gn)&&this._element.classList.remove(Gn),g(this._element).off(zn.CLICK_DISMISS),g.removeData(this._element,Bn),this._element=null,this._config=null},t._getConfig=function(t){return t=l({},ti,g(this._element).data(),"object"==typeof t&&t?t:{}),_.typeCheckConfig(Qn,t,this.constructor.DefaultType),t},t._setListeners=function(){var t=this;g(this._element).on(zn.CLICK_DISMISS,ei,function(){return t.hide(!0)})},t._close=function(){var t=this,e=function(){t._element.classList.add($n),g(t._element).trigger(zn.HIDDEN)};if(this._element.classList.remove(Gn),this._config.animation){var n=_.getTransitionDurationFromElement(this._element);g(this._element).one(_.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},i._jQueryInterface=function(n){return this.each(function(){var t=g(this),e=t.data(Bn);if(e||(e=new i(this,"object"==typeof n&&n),t.data(Bn,e)),"string"==typeof n){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n](this)}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"DefaultType",get:function(){return Zn}},{key:"Default",get:function(){return ti}}]),i}();g.fn[Qn]=ni._jQueryInterface,g.fn[Qn].Constructor=ni,g.fn[Qn].noConflict=function(){return g.fn[Qn]=Yn,ni._jQueryInterface},function(){if("undefined"==typeof g)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=g.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||4<=t[0])throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(),t.Util=_,t.Alert=p,t.Button=P,t.Carousel=lt,t.Collapse=bt,t.Dropdown=Jt,t.Modal=ve,t.Popover=sn,t.Scrollspy=Dn,t.Tab=Kn,t.Toast=ni,t.Tooltip=Be,Object.defineProperty(t,"__esModule",{value:!0})});
//# sourceMappingURL=bootstrap.min.js.map/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}k.fn=k.prototype={jquery:f,constructor:k,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=k.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return k.each(this,e)},map:function(n){return this.pushStack(k.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},k.extend=k.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(k.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||k.isPlainObject(n)?n:{},i=!1,a[t]=k.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},k.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t){b(e,{nonce:t&&t.nonce})},each:function(e,t){var n,r=0;if(d(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(p,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(d(Object(e))?k.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(d(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g.apply([],a)},guid:1,support:y}),"function"==typeof Symbol&&(k.fn[Symbol.iterator]=t[Symbol.iterator]),k.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var h=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,k="sizzle"+1*new Date,m=n.document,S=0,r=0,p=ue(),x=ue(),N=ue(),A=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",$=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",F=new RegExp(M+"+","g"),B=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),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("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=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)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(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 de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){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[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.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},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="<a id='"+k+"'></a><select id='"+k+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.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 l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(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?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!==C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!==C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,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(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===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]||se.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]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(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(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(F," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===S&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[S,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===S&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[k]||(a[k]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[S,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[k]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace(B,"$1"));return s[k]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!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!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.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:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[S,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[k]||(e[k]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===S&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[k]&&(v=Ce(v)),y&&!y[k]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[k]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(B,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace(B," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=N[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[k]?i.push(a):o.push(a);(a=N(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=S+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t===C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument===C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(S=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(S=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=k.split("").sort(D).join("")===k,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);k.find=h,k.expr=h.selectors,k.expr[":"]=k.expr.pseudos,k.uniqueSort=k.unique=h.uniqueSort,k.text=h.getText,k.isXMLDoc=h.isXML,k.contains=h.contains,k.escapeSelector=h.escape;var T=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&k(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},N=k.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var D=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1<i.call(n,e)!==r}):k.filter(n,e,r)}k.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?k.find.matchesSelector(r,e)?[r]:[]:k.find.matches(e,k.grep(t,function(e){return 1===e.nodeType}))},k.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(k(e).filter(function(){for(t=0;t<r;t++)if(k.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)k.find(e,i[t],n);return 1<r?k.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&&N.test(e)?k(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(k.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&k(e);if(!N.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&k.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?k.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(k(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(k.uniqueSort(k.merge(this.get(),k(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),k.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return T(e,"parentNode")},parentsUntil:function(e,t,n){return T(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return T(e,"nextSibling")},prevAll:function(e){return T(e,"previousSibling")},nextUntil:function(e,t,n){return T(e,"nextSibling",n)},prevUntil:function(e,t,n){return T(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return"undefined"!=typeof e.contentDocument?e.contentDocument:(A(e,"template")&&(e=e.content||e),k.merge([],e.childNodes))}},function(r,i){k.fn[r]=function(e,t){var n=k.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=k.filter(t,n)),1<this.length&&(O[r]||k.uniqueSort(n),H.test(r)&&n.reverse()),this.pushStack(n)}});var R=/[^\x20\t\r\n\f]+/g;function M(e){return e}function I(e){throw e}function W(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}k.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},k.each(e.match(R)||[],function(e,t){n[t]=!0}),n):k.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){k.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return k.each(arguments,function(e,t){var n;while(-1<(n=k.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<k.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},k.extend({Deferred:function(e){var o=[["notify","progress",k.Callbacks("memory"),k.Callbacks("memory"),2],["resolve","done",k.Callbacks("once memory"),k.Callbacks("once memory"),0,"resolved"],["reject","fail",k.Callbacks("once memory"),k.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return k.Deferred(function(r){k.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,M,s),l(u,o,I,s)):(u++,t.call(e,l(u,o,M,s),l(u,o,I,s),l(u,o,M,o.notifyWith))):(a!==M&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){k.Deferred.exceptionHook&&k.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==I&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(k.Deferred.getStackHook&&(t.stackTrace=k.Deferred.getStackHook()),C.setTimeout(t))}}return k.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:M,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:M)),o[2][3].add(l(0,e,m(n)?n:I))}).promise()},promise:function(e){return null!=e?k.extend(e,a):a}},s={};return k.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=k.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(W(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)W(i[t],a(t),o.reject);return o.promise()}});var $=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;k.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&$.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},k.readyException=function(e){C.setTimeout(function(){throw e})};var F=k.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),k.ready()}k.fn.ready=function(e){return F.then(e)["catch"](function(e){k.readyException(e)}),this},k.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--k.readyWait:k.isReady)||(k.isReady=!0)!==e&&0<--k.readyWait||F.resolveWith(E,[k])}}),k.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(k.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var _=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)_(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(k(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},z=/^-ms-/,U=/-([a-z])/g;function X(e,t){return t.toUpperCase()}function V(e){return e.replace(z,"ms-").replace(U,X)}var G=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function Y(){this.expando=k.expando+Y.uid++}Y.uid=1,Y.prototype={cache:function(e){var t=e[this.expando];return t||(t={},G(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[V(t)]=n;else for(r in t)i[V(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][V(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(V):(t=V(t))in r?[t]:t.match(R)||[]).length;while(n--)delete r[t[n]]}(void 0===t||k.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!k.isEmptyObject(t)}};var Q=new Y,J=new Y,K=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function ee(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(Z,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:K.test(i)?JSON.parse(i):i)}catch(e){}J.set(e,t,n)}else n=void 0;return n}k.extend({hasData:function(e){return J.hasData(e)||Q.hasData(e)},data:function(e,t,n){return J.access(e,t,n)},removeData:function(e,t){J.remove(e,t)},_data:function(e,t,n){return Q.access(e,t,n)},_removeData:function(e,t){Q.remove(e,t)}}),k.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=J.get(o),1===o.nodeType&&!Q.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=V(r.slice(5)),ee(o,r,i[r]));Q.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){J.set(this,n)}):_(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=J.get(o,n))?t:void 0!==(t=ee(o,n))?t:void 0;this.each(function(){J.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){J.remove(this,e)})}}),k.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Q.get(e,t),n&&(!r||Array.isArray(n)?r=Q.access(e,t,k.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=k.queue(e,t),r=n.length,i=n.shift(),o=k._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){k.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Q.get(e,n)||Q.access(e,n,{empty:k.Callbacks("once memory").add(function(){Q.remove(e,[t+"queue",n])})})}}),k.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?k.queue(this[0],t):void 0===n?this:this.each(function(){var e=k.queue(this,t,n);k._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&k.dequeue(this,t)})},dequeue:function(e){return this.each(function(){k.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=k.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Q.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var te=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ne=new RegExp("^(?:([+-])=|)("+te+")([a-z%]*)$","i"),re=["Top","Right","Bottom","Left"],ie=E.documentElement,oe=function(e){return k.contains(e.ownerDocument,e)},ae={composed:!0};ie.getRootNode&&(oe=function(e){return k.contains(e.ownerDocument,e)||e.getRootNode(ae)===e.ownerDocument});var se=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&oe(e)&&"none"===k.css(e,"display")},ue=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];for(o in i=n.apply(e,r||[]),t)e.style[o]=a[o];return i};function le(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return k.css(e,t,"")},u=s(),l=n&&n[3]||(k.cssNumber[t]?"":"px"),c=e.nodeType&&(k.cssNumber[t]||"px"!==l&&+u)&&ne.exec(k.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)k.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,k.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ce={};function fe(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Q.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&se(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ce[s])||(o=a.body.appendChild(a.createElement(s)),u=k.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ce[s]=u)))):"none"!==n&&(l[c]="none",Q.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}k.fn.extend({show:function(){return fe(this,!0)},hide:function(){return fe(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){se(this)?k(this).show():k(this).hide()})}});var pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Q.set(e[n],"globalEval",!t||Q.get(t[n],"globalEval"))}ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;var me,xe,be=/<|&#?\w+;/;function we(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))k.merge(p,o.nodeType?[o]:o);else if(be.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+k.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;k.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<k.inArray(o,r))i&&i.push(o);else if(l=oe(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}me=E.createDocumentFragment().appendChild(E.createElement("div")),(xe=E.createElement("input")).setAttribute("type","radio"),xe.setAttribute("checked","checked"),xe.setAttribute("name","t"),me.appendChild(xe),y.checkClone=me.cloneNode(!0).cloneNode(!0).lastChild.checked,me.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(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=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.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,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t<arguments.length;t++)u[t]=arguments[t];if(s.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,s)){a=k.event.handlers.call(this,s,l),t=0;while((i=a[t++])&&!s.isPropagationStopped()){s.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!s.isImmediatePropagationStopped())s.rnamespace&&!1!==o.namespace&&!s.rnamespace.test(o.namespace)||(s.handleObj=o,s.data=o.data,void 0!==(r=((k.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,u))&&!1===(s.result=r)&&(s.preventDefault(),s.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,s),s.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<k(i,this).index(l):k.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(k.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[k.expando]?e:new k.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&De(t,"click",ke),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&De(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Q.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},k.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},k.Event=function(e,t){if(!(this instanceof k.Event))return new k.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?ke:Se,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&k.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[k.expando]=!0},k.Event.prototype={constructor:k.Event,isDefaultPrevented:Se,isPropagationStopped:Se,isImmediatePropagationStopped:Se,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=ke,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=ke,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=ke,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},k.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&Te.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&Ce.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},k.event.addProp),k.each({focus:"focusin",blur:"focusout"},function(e,t){k.event.special[e]={setup:function(){return De(this,e,Ne),!1},trigger:function(){return De(this,e),!0},delegateType:t}}),k.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){k.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||k.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),k.fn.extend({on:function(e,t,n,r){return Ae(this,e,t,n,r)},one:function(e,t,n,r){return Ae(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,k(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Se),this.each(function(){k.event.remove(this,e,n,t)})}});var je=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/<script|<style|<link/i,Le=/checked\s*(?:[^=]|=\s*.checked.)/i,He=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n<r;n++)k.event.add(t,i,l[i][n]);J.hasData(e)&&(s=J.access(e),u=k.extend({},s),J.set(t,u))}}function Ie(n,r,i,o){r=g.apply([],r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&Le.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Ie(t,r,i,o)});if(f&&(t=(e=we(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=k.map(ve(e,"script"),Pe)).length;c<f;c++)u=e,c!==p&&(u=k.clone(u,!0,!0),s&&k.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,k.map(a,Re),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Q.access(u,"globalEval")&&k.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?k._evalUrl&&!u.noModule&&k._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")}):b(u.textContent.replace(He,""),u,l))}return n}function We(e,t,n){for(var r,i=t?k.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||k.cleanData(ve(r)),r.parentNode&&(n&&oe(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}k.extend({htmlPrefilter:function(e){return e.replace(je,"<$1></$2>")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Me(o[r],a[r]);else Me(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=k.event.special,o=0;void 0!==(n=e[o]);o++)if(G(n)){if(t=n[Q.expando]){if(t.events)for(r in t.events)i[r]?k.event.remove(n,r):k.removeEvent(n,r,t.handle);n[Q.expando]=void 0}n[J.expando]&&(n[J.expando]=void 0)}}}),k.fn.extend({detach:function(e){return We(this,e,!0)},remove:function(e){return We(this,e)},text:function(e){return _(this,function(e){return void 0===e?k.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 Ie(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Oe(this,e).appendChild(e)})},prepend:function(){return Ie(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Oe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Ie(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Ie(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&&(k.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return k.clone(this,e,t)})},html:function(e){return _(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&&!qe.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=k.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(k.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Ie(this,arguments,function(e){var t=this.parentNode;k.inArray(this,n)<0&&(k.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),k.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){k.fn[e]=function(e){for(var t,n=[],r=k(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),k(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var $e=new RegExp("^("+te+")(?!px)[a-z%]+$","i"),Fe=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},Be=new RegExp(re.join("|"),"i");function _e(e,t,n){var r,i,o,a,s=e.style;return(n=n||Fe(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||oe(e)||(a=k.style(e,t)),!y.pixelBoxStyles()&&$e.test(a)&&Be.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function ze(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(u){s.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",u.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",ie.appendChild(s).appendChild(u);var e=C.getComputedStyle(u);n="1%"!==e.top,a=12===t(e.marginLeft),u.style.right="60%",o=36===t(e.right),r=36===t(e.width),u.style.position="absolute",i=12===t(u.offsetWidth/3),ie.removeChild(s),u=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s=E.createElement("div"),u=E.createElement("div");u.style&&(u.style.backgroundClip="content-box",u.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===u.style.backgroundClip,k.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),a},scrollboxSize:function(){return e(),i}}))}();var Ue=["Webkit","Moz","ms"],Xe=E.createElement("div").style,Ve={};function Ge(e){var t=k.cssProps[e]||Ve[e];return t||(e in Xe?e:Ve[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Ue.length;while(n--)if((e=Ue[n]+t)in Xe)return e}(e)||e)}var Ye=/^(none|table(?!-c[ea]).+)/,Qe=/^--/,Je={position:"absolute",visibility:"hidden",display:"block"},Ke={letterSpacing:"0",fontWeight:"400"};function Ze(e,t,n){var r=ne.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function et(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=k.css(e,n+re[a],!0,i)),r?("content"===n&&(u-=k.css(e,"padding"+re[a],!0,i)),"margin"!==n&&(u-=k.css(e,"border"+re[a]+"Width",!0,i))):(u+=k.css(e,"padding"+re[a],!0,i),"padding"!==n?u+=k.css(e,"border"+re[a]+"Width",!0,i):s+=k.css(e,"border"+re[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function tt(e,t,n){var r=Fe(e),i=(!y.boxSizingReliable()||n)&&"border-box"===k.css(e,"boxSizing",!1,r),o=i,a=_e(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if($e.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||"auto"===a||!parseFloat(a)&&"inline"===k.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===k.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+et(e,t,n||(i?"border":"content"),o,r,a)+"px"}function nt(e,t,n,r,i){return new nt.prototype.init(e,t,n,r,i)}k.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=_e(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!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=V(t),u=Qe.test(t),l=e.style;if(u||(t=Ge(s)),a=k.cssHooks[t]||k.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=ne.exec(n))&&i[1]&&(n=le(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(k.cssNumber[s]?"":"px")),y.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=V(t);return Qe.test(t)||(t=Ge(s)),(a=k.cssHooks[t]||k.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=_e(e,t,r)),"normal"===i&&t in Ke&&(i=Ke[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),k.each(["height","width"],function(e,u){k.cssHooks[u]={get:function(e,t,n){if(t)return!Ye.test(k.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?tt(e,u,n):ue(e,Je,function(){return tt(e,u,n)})},set:function(e,t,n){var r,i=Fe(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===k.css(e,"boxSizing",!1,i),s=n?et(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-et(e,u,"border",!1,i)-.5)),s&&(r=ne.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=k.css(e,u)),Ze(0,t,s)}}}),k.cssHooks.marginLeft=ze(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(_e(e,"marginLeft"))||e.getBoundingClientRect().left-ue(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),k.each({margin:"",padding:"",border:"Width"},function(i,o){k.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+re[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(k.cssHooks[i+o].set=Ze)}),k.fn.extend({css:function(e,t){return _(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Fe(e),i=t.length;a<i;a++)o[t[a]]=k.css(e,t[a],!1,r);return o}return void 0!==n?k.style(e,t,n):k.css(e,t)},e,t,1<arguments.length)}}),((k.Tween=nt).prototype={constructor:nt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||k.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(k.cssNumber[n]?"":"px")},cur:function(){var e=nt.propHooks[this.prop];return e&&e.get?e.get(this):nt.propHooks._default.get(this)},run:function(e){var t,n=nt.propHooks[this.prop];return this.options.duration?this.pos=t=k.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):nt.propHooks._default.set(this),this}}).init.prototype=nt.prototype,(nt.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=k.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){k.fx.step[e.prop]?k.fx.step[e.prop](e):1!==e.elem.nodeType||!k.cssHooks[e.prop]&&null==e.elem.style[Ge(e.prop)]?e.elem[e.prop]=e.now:k.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=nt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},k.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},k.fx=nt.prototype.init,k.fx.step={};var rt,it,ot,at,st=/^(?:toggle|show|hide)$/,ut=/queueHooks$/;function lt(){it&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(lt):C.setTimeout(lt,k.fx.interval),k.fx.tick())}function ct(){return C.setTimeout(function(){rt=void 0}),rt=Date.now()}function ft(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=re[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function pt(e,t,n){for(var r,i=(dt.tweeners[t]||[]).concat(dt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function dt(o,e,t){var n,a,r=0,i=dt.prefilters.length,s=k.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=rt||ct(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:k.extend({},e),opts:k.extend(!0,{specialEasing:{},easing:k.easing._default},t),originalProperties:e,originalOptions:t,startTime:rt||ct(),duration:t.duration,tweens:[],createTween:function(e,t){var n=k.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=V(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=k.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=dt.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(k._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return k.map(c,pt,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),k.fx.timer(k.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}k.Animation=k.extend(dt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return le(n.elem,e,ne.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(R);for(var n,r=0,i=e.length;r<i;r++)n=e[r],dt.tweeners[n]=dt.tweeners[n]||[],dt.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&se(e),v=Q.get(e,"fxshow");for(r in n.queue||(null==(a=k._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,k.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],st.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||k.style(e,r)}if((u=!k.isEmptyObject(t))||!k.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Q.get(e,"display")),"none"===(c=k.css(e,"display"))&&(l?c=l:(fe([e],!0),l=e.style.display||l,c=k.css(e,"display"),fe([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===k.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Q.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&fe([e],!0),p.done(function(){for(r in g||fe([e]),Q.remove(e,"fxshow"),d)k.style(e,r,d[r])})),u=pt(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?dt.prefilters.unshift(e):dt.prefilters.push(e)}}),k.speed=function(e,t,n){var r=e&&"object"==typeof e?k.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return k.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in k.fx.speeds?r.duration=k.fx.speeds[r.duration]:r.duration=k.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&k.dequeue(this,r.queue)},r},k.fn.extend({fadeTo:function(e,t,n,r){return this.filter(se).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=k.isEmptyObject(t),o=k.speed(e,n,r),a=function(){var e=dt(this,k.extend({},t),o);(i||Q.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&!1!==i&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=k.timers,r=Q.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&ut.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||k.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Q.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=k.timers,o=n?n.length:0;for(t.finish=!0,k.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),k.each(["toggle","show","hide"],function(e,r){var i=k.fn[r];k.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(ft(r,!0),e,t,n)}}),k.each({slideDown:ft("show"),slideUp:ft("hide"),slideToggle:ft("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){k.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),k.timers=[],k.fx.tick=function(){var e,t=0,n=k.timers;for(rt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||k.fx.stop(),rt=void 0},k.fx.timer=function(e){k.timers.push(e),k.fx.start()},k.fx.interval=13,k.fx.start=function(){it||(it=!0,lt())},k.fx.stop=function(){it=null},k.fx.speeds={slow:600,fast:200,_default:400},k.fn.delay=function(r,e){return r=k.fx&&k.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},ot=E.createElement("input"),at=E.createElement("select").appendChild(E.createElement("option")),ot.type="checkbox",y.checkOn=""!==ot.value,y.optSelected=at.selected,(ot=E.createElement("input")).value="t",ot.type="radio",y.radioValue="t"===ot.value;var ht,gt=k.expr.attrHandle;k.fn.extend({attr:function(e,t){return _(this,k.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){k.removeAttr(this,e)})}}),k.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?k.prop(e,t,n):(1===o&&k.isXMLDoc(e)||(i=k.attrHooks[t.toLowerCase()]||(k.expr.match.bool.test(t)?ht:void 0)),void 0!==n?null===n?void k.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=k.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(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(R);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),ht={set:function(e,t,n){return!1===t?k.removeAttr(e,n):e.setAttribute(n,n),n}},k.each(k.expr.match.bool.source.match(/\w+/g),function(e,t){var a=gt[t]||k.find.attr;gt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=gt[o],gt[o]=r,r=null!=a(e,t,n)?o:null,gt[o]=i),r}});var vt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;function mt(e){return(e.match(R)||[]).join(" ")}function xt(e){return e.getAttribute&&e.getAttribute("class")||""}function bt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(R)||[]}k.fn.extend({prop:function(e,t){return _(this,k.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[k.propFix[e]||e]})}}),k.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&k.isXMLDoc(e)||(t=k.propFix[t]||t,i=k.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=k.find.attr(e,"tabindex");return t?parseInt(t,10):vt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(k.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)}}),k.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){k.propFix[this.toLowerCase()]=this}),k.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){k(this).addClass(t.call(this,e,xt(this)))});if((e=bt(t)).length)while(n=this[u++])if(i=xt(n),r=1===n.nodeType&&" "+mt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=mt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){k(this).removeClass(t.call(this,e,xt(this)))});if(!arguments.length)return this.attr("class","");if((e=bt(t)).length)while(n=this[u++])if(i=xt(n),r=1===n.nodeType&&" "+mt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=mt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){k(this).toggleClass(i.call(this,e,xt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=k(this),r=bt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=xt(this))&&Q.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Q.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+mt(xt(n))+" ").indexOf(t))return!0;return!1}});var wt=/\r/g;k.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,k(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=k.map(t,function(e){return null==e?"":e+""})),(r=k.valHooks[this.type]||k.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=k.valHooks[t.type]||k.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(wt,""):null==e?"":e:void 0}}),k.extend({valHooks:{option:{get:function(e){var t=k.find.attr(e,"value");return null!=t?t:mt(k.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<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=k(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=k.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<k.inArray(k.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),k.each(["radio","checkbox"],function(){k.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<k.inArray(k(e).val(),t)}},y.checkOn||(k.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var Tt=/^(?:focusinfocus|focusoutblur)$/,Ct=function(e){e.stopPropagation()};k.extend(k.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!Tt.test(d+k.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[k.expando]?e:new k.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:k.makeArray(t,[e]),c=k.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,Tt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Q.get(o,"events")||{})[e.type]&&Q.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&G(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!G(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),k.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,Ct),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,Ct),k.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=k.extend(new k.Event,n,{type:e,isSimulated:!0});k.event.trigger(r,null,t)}}),k.fn.extend({trigger:function(e,t){return this.each(function(){k.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return k.event.trigger(e,t,n,!0)}}),y.focusin||k.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){k.event.simulate(r,e.target,k.event.fix(e))};k.event.special[r]={setup:function(){var e=this.ownerDocument||this,t=Q.access(e,r);t||e.addEventListener(n,i,!0),Q.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this,t=Q.access(e,r)-1;t?Q.access(e,r,t):(e.removeEventListener(n,i,!0),Q.remove(e,r))}}});var Et=C.location,kt=Date.now(),St=/\?/;k.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||k.error("Invalid XML: "+e),t};var Nt=/\[\]$/,At=/\r?\n/g,Dt=/^(?:submit|button|image|reset|file)$/i,jt=/^(?:input|select|textarea|keygen)/i;function qt(n,e,r,i){var t;if(Array.isArray(e))k.each(e,function(e,t){r||Nt.test(n)?i(n,t):qt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)qt(n+"["+t+"]",e[t],r,i)}k.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!k.isPlainObject(e))k.each(e,function(){i(this.name,this.value)});else for(n in e)qt(n,e[n],t,i);return r.join("&")},k.fn.extend({serialize:function(){return k.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=k.prop(this,"elements");return e?k.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!k(this).is(":disabled")&&jt.test(this.nodeName)&&!Dt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=k(this).val();return null==n?null:Array.isArray(n)?k.map(n,function(e){return{name:t.name,value:e.replace(At,"\r\n")}}):{name:t.name,value:n.replace(At,"\r\n")}}).get()}});var Lt=/%20/g,Ht=/#.*$/,Ot=/([?&])_=[^&]*/,Pt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Rt=/^(?:GET|HEAD)$/,Mt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Ft=E.createElement("a");function Bt(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(R)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function _t(t,i,o,a){var s={},u=t===Wt;function l(e){var r;return s[e]=!0,k.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function zt(e,t){var n,r,i=k.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&k.extend(!0,e,r),e}Ft.href=Et.href,k.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Et.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Et.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":k.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,k.ajaxSettings),t):zt(k.ajaxSettings,e)},ajaxPrefilter:Bt(It),ajaxTransport:Bt(Wt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=k.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?k(y):k.event,x=k.Deferred(),b=k.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Pt.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Et.href)+"").replace(Mt,Et.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(R)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Ft.protocol+"//"+Ft.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=k.param(v.data,v.traditional)),_t(It,v,t,T),h)return T;for(i in(g=k.event&&v.global)&&0==k.active++&&k.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Rt.test(v.type),f=v.url.replace(Ht,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(Lt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(St.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Ot,"$1"),o=(St.test(f)?"&":"?")+"_="+kt+++o),v.url=f+o),v.ifModified&&(k.lastModified[f]&&T.setRequestHeader("If-Modified-Since",k.lastModified[f]),k.etag[f]&&T.setRequestHeader("If-None-Match",k.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+$t+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=_t(Wt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(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]}(v,T,n)),s=function(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}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(k.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(k.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--k.active||k.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return k.get(e,t,n,"json")},getScript:function(e,t){return k.get(e,void 0,t,"script")}}),k.each(["get","post"],function(e,i){k[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),k.ajax(k.extend({url:e,type:i,dataType:r,data:t,success:n},k.isPlainObject(e)&&e))}}),k._evalUrl=function(e,t){return k.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){k.globalEval(e,t)}})},k.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=k(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(n){return m(n)?this.each(function(e){k(this).wrapInner(n.call(this,e))}):this.each(function(){var e=k(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){k(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){k(this).replaceWith(this.childNodes)}),this}}),k.expr.pseudos.hidden=function(e){return!k.expr.pseudos.visible(e)},k.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},k.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var Ut={0:200,1223:204},Xt=k.ajaxSettings.xhr();y.cors=!!Xt&&"withCredentials"in Xt,y.ajax=Xt=!!Xt,k.ajaxTransport(function(i){var o,a;if(y.cors||Xt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(Ut[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),k.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),k.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 k.globalEval(e),e}}}),k.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),k.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=k("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=mt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&k.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?k("<div>").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}}),k.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),k.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),k.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||k.guid++,i},k.holdReady=function(e){e?k.readyWait++:k.ready(!0)},k.isArray=Array.isArray,k.parseJSON=JSON.parse,k.nodeName=A,k.isFunction=m,k.isWindow=x,k.camelCase=V,k.type=w,k.now=Date.now,k.isNumeric=function(e){var t=k.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},"function"==typeof define&&define.amd&&define("jquery",[],function(){return k});var Qt=C.jQuery,Jt=C.$;return k.noConflict=function(e){return C.$===k&&(C.$=Jt),e&&C.jQuery===k&&(C.jQuery=Qt),k},e||(C.jQuery=C.$=k),k});
/* nvd3 version 1.8.1 (https://github.com/novus/nvd3) 2015-06-15 */
!function(){var a={};a.dev=!1,a.tooltip=a.tooltip||{},a.utils=a.utils||{},a.models=a.models||{},a.charts={},a.logs={},a.dom={},a.dispatch=d3.dispatch("render_start","render_end"),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),a.dev&&(a.dispatch.on("render_start",function(){a.logs.startTime=+new Date}),a.dispatch.on("render_end",function(){a.logs.endTime=+new Date,a.logs.totalTime=a.logs.endTime-a.logs.startTime,a.log("total",a.logs.totalTime)})),a.log=function(){if(a.dev&&window.console&&console.log&&console.log.apply)console.log.apply(console,arguments);else if(a.dev&&window.console&&"function"==typeof console.log&&Function.prototype.bind){var b=Function.prototype.bind.call(console.log,console);b.apply(console,arguments)}return arguments[arguments.length-1]},a.deprecated=function(a,b){console&&console.warn&&console.warn("nvd3 warning: `"+a+"` has been deprecated. ",b||"")},a.render=function(b){b=b||1,a.render.active=!0,a.dispatch.render_start();var c=function(){for(var d,e,f=0;b>f&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+="<div class='footer'>"+a.footer+"</div>"),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;e<c.length;e+=1){var f=c[e]&&c[e].values?c[e].values.length:0;d=f>d?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)<d.range()[1]+10||d(a)>d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)<E[0]||d(a)>E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g");
x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f);
var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&C<B.length;)E[C]=B[C],D+=B[C++];for(0===C&&(C=1);D>p&&C>1;){E=[],C--;for(var F=0;F<B.length;F++)B[F]>(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,d<c.left+c.right+J+a&&(L=J=5,K+=A),L+=a,L>M&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open<c.close?"2ca02c":"d62728";return'<h3 style="color: #'+d+'">'+a.value+"</h3><table><tr><td>open:</td><td>"+b.yAxis.tickFormat()(c.open)+"</td></tr><tr><td>close:</td><td>"+b.yAxis.tickFormat()(c.close)+"</td></tr><tr><td>high</td><td>"+b.yAxis.tickFormat()(c.high)+"</td></tr><tr><td>low:</td><td>"+b.yAxis.tickFormat()(c.low)+"</td></tr></table>"}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open<c.close?"2ca02c":"d62728";return'<h3 style="color: #'+d+'">'+a.value+"</h3><table><tr><td>open:</td><td>"+b.yAxis.tickFormat()(c.open)+"</td></tr><tr><td>close:</td><td>"+b.yAxis.tickFormat()(c.close)+"</td></tr><tr><td>high</td><td>"+b.yAxis.tickFormat()(c.high)+"</td></tr><tr><td>low:</td><td>"+b.yAxis.tickFormat()(c.low)+"</td></tr></table>"}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&E<D.length;)F[E]=D[E],C+=D[E++];for(0===E&&(E=1);C>g&&E>1;){F=[],E--;for(var G=0;G<D.length;G++)D[G]>(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,d<c.left+c.right+K+a&&(M=K=5,L+=x),M+=a,M>N&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D]
}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(E){return C.reset(),E.each(function(b){var E=k-j.left-j.right,F=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var G=0;if(x&&b.length&&(x=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),u){var H=d3.layout.stack().offset(v).values(function(a){return a.values}).y(r)(!b.length&&x?x:b);H.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=G++,H[c]=b[c]):c>0&&H[c-1].nonStackable&&H[c].values.map(function(a,b){a.y0-=H[c-1].values[b].y,a.y1=a.y0+a.y})}),b=H}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var I=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b}})});m.domain(d||d3.merge(I).map(function(a){return a.x})).rangeBands(f||[0,E],A),n.domain(e||d3.extent(d3.merge(I).map(function(a){var c=a.y;return u&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y),c}).concat(s))).range(g||[F,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var J=p.selectAll("g.nv-wrap.nv-multibar").data([b]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),L=K.append("defs"),M=K.append("g"),N=J.select("g");M.append("g").attr("class","nv-groups"),J.attr("transform","translate("+j.left+","+j.top+")"),L.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),J.select("#nv-edge-clip-"+o+" rect").attr("width",E).attr("height",F),N.attr("clip-path",t?"url(#nv-edge-clip-"+o+")":"");var O=J.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});O.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var P=C.transition(O.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(100,z)).attr("y",function(a){var c=i(0)||0;return u&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();P.delay&&P.delay(function(a,b){var c=b*(z/(D+1))-b;return c}),O.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return w(a,b)}).style("stroke",function(a,b){return w(a,b)}),O.style("stroke-opacity",1).style("fill-opacity",.75);var Q=O.selectAll("rect.nv-bar").data(function(a){return x&&!b.length?x.values:a.values});Q.exit().remove();Q.enter().append("rect").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return u&&!b[d].nonStackable?0:d*m.rangeBand()/b.length}).attr("y",function(a,c,d){return i(u&&!b[d].nonStackable?a.y0:0)||0}).attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(u&&!b[d].nonStackable?1:b.length)}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"});Q.style("fill",function(a,b,c){return w(a,c,b)}).style("stroke",function(a,b,c){return w(a,c,b)}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),B.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),B.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){B.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){B.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){B.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),Q.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"}),y&&(c||(c=b.map(function(){return!0})),Q.style("fill",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var R=Q.watchTransition(C,"multibar",Math.min(250,z)).delay(function(a,c){return c*z/b[0].values.length});u?R.attr("y",function(a,c,d){var e=0;return e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1)}).attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("x",function(a,c,d){var e=0;return b[d].nonStackable&&(e=a.series*m.rangeBand()/b.length,b.length!==G&&(e=b[d].nonStackableSeries*m.rangeBand()/(2*G))),e}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/G;return b.length!==G&&(e=m.rangeBand()/(2*G)),e}return m.rangeBand()}):R.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return r(a,b)<0?n(0):n(0)-n(r(a,b))<1?n(0)-1:n(r(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(D=b[0].values.length)}),C.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=[0],t=!0,u=!1,v="zero",w=a.utils.defaultColor(),x=!1,y=null,z=500,A=.1,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),C=a.utils.renderWatch(B,z),D=0;return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return s},set:function(a){s=a}},stacked:{get:function(){return u},set:function(a){u=a}},stackOffset:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return t},set:function(a){t=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return x},set:function(a){x=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z)}},color:{get:function(){return w},set:function(b){w=a.utils.getColor(b)}},barColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();
var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return E.reset(),m.each(function(b){var m=k-j.left-j.right,C=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),w&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),w&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1}})});o.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],A),p.domain(e||d3.extent(d3.merge(F).map(function(a){return w?a.y>0?a.y1+a.y:a.y1:a.y}).concat(t))),p.range(x&&!w?g||[p.domain()[0]<0?z:0,m-(p.domain()[1]>0?z:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),I=(H.append("defs"),H.append("g"));G.select("g")}I.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")");var J=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});J.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),J.exit().watchTransition(E,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),J.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),J.watchTransition(E,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var K=J.selectAll("g.nv-bar").data(function(a){return a.values});K.exit().remove();var L=K.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(w?a.y0:0)+","+(w?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});L.append("rect").attr("width",0).attr("height",o.rangeBand()/(w?1:b.length)),K.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0],0)&&(L.append("polyline"),K.select("polyline").attr("fill","none").attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(w?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(w?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),L.append("text"),x&&!w?(K.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=B(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+B(Math.abs(d[1]))+"-"+B(Math.abs(d[0])):c+"±"+B(Math.abs(d))}),K.watchTransition(E,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):K.selectAll("text").text(""),y&&!w?(L.append("text").classed("nv-bar-label",!0),K.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),K.watchTransition(E,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):K.selectAll("text.nv-bar-label").text(""),K.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),K.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),w?K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),E.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=!1,x=!1,y=!1,z=60,A=.1,B=d3.format(",.2f"),C=250,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,C);return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return w},set:function(a){w=a}},showValues:{get:function(){return x},set:function(a){x=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return B},set:function(a){B=a}},valuePadding:{get:function(){return z},set:function(a){z=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return C},set:function(a){C=a,E.reset(C)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.point.x=v.x()(a.point),a.point.y=v.y()(a.point),B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.data.series].yAxis?z:y;a.value=t.x()(a.data),a.series={value:t.y()(a.data),color:a.color},B.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var C=d3.select(this);a.utils.initSVG(C),b.update=function(){C.transition().call(b)},b.container=this;var D=a.utils.availableWidth(g,C,e),E=a.utils.availableHeight(h,C,e),F=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),G=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),H=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),I=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),J=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),K=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,C),b;C.selectAll(".nv-noData").remove();var L=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=C.selectAll("g.wrap.multiChart").data([j]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y1 nv-axis"),O.append("g").attr("class","nv-y2 nv-axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g"),Q=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var R=A.align()?D/2:D,S=A.align()?R:0;A.width(R),A.color(Q),P.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),e.top!=A.height()&&(e.top=A.height(),E=a.utils.availableHeight(h,C,e)),P.select(".legendWrap").attr("transform","translate("+S+","+-e.top+")")}r.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),u.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),v.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),w.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),P.attr("transform","translate("+e.left+","+e.top+")");var T=P.select(".lines1Wrap").datum(F.filter(function(a){return!a.disabled})),U=P.select(".bars1Wrap").datum(H.filter(function(a){return!a.disabled})),V=P.select(".stack1Wrap").datum(J.filter(function(a){return!a.disabled})),W=P.select(".lines2Wrap").datum(G.filter(function(a){return!a.disabled})),X=P.select(".bars2Wrap").datum(I.filter(function(a){return!a.disabled})),Y=P.select(".stack2Wrap").datum(K.filter(function(a){return!a.disabled})),Z=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],$=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(L).concat(Z),function(a){return a.y})).range([0,E]),q.domain(d||d3.extent(d3.merge(M).concat($),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(V).call(v),K.length&&d3.transition(Y).call(w),H.length&&d3.transition(U).call(t),I.length&&d3.transition(X).call(u),F.length&&d3.transition(T).call(r),G.length&&d3.transition(W).call(s),x._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-E,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".nv-x.nv-axis")).call(x),y._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y1.nv-axis")).call(y),z._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y2.nv-axis")).call(z),P.select(".nv-y1.nv-axis").classed("nv-disabled",L.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),P.select(".nv-y2.nv-axis").classed("nv-disabled",M.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",l),w.dispatch.on("elementMouseover.tooltip",l),v.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",n),u.dispatch.on("elementMouseover.tooltip",n),t.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()}),u.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(o).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=a.models.tooltip(),C=d3.dispatch();return b.dispatch=C,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.tooltip=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return B.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),B.enabled(!!b)}},tooltipContent:{get:function(){return B.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),B.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),v.y(a),w.y(a),t.y(a),u.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),v.useVoronoi(a),w.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left
}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;M<b[0].length;M++)I.push(K),J.push(L);else I=z.map(function(a){return(a.outer-a.outer/5)*H}),J=z.map(function(a){return(a.inner-a.inner/5)*H}),y=d3.min(z.map(function(a){return a.inner-a.inner/5}));a.utils.initSVG(i);var N=i.selectAll(".nv-wrap.nv-pie").data(b),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-pie nv-chart-"+h),P=O.append("g"),Q=N.select("g"),R=P.append("g").attr("class","nv-pie");P.append("g").attr("class","nv-pieLabels"),N.attr("transform","translate("+c.left+","+c.top+")"),Q.select(".nv-pie").attr("transform","translate("+F/2+","+G/2+")"),Q.select(".nv-pieLabels").attr("transform","translate("+F/2+","+G/2+")"),i.on("click",function(a,b){A.chartClick({data:a,index:b,pos:d3.event,id:h})}),B=[],C=[];for(var M=0;M<b[0].length;M++){var S=d3.svg.arc().outerRadius(I[M]),T=d3.svg.arc().outerRadius(I[M]+5);u!==!1&&(S.startAngle(u),T.startAngle(u)),w!==!1&&(S.endAngle(w),T.endAngle(w)),p&&(S.innerRadius(J[M]),T.innerRadius(J[M])),S.cornerRadius&&x&&(S.cornerRadius(x),T.cornerRadius(x)),B.push(S),C.push(T)}var U=d3.layout.pie().sort(null).value(function(a){return a.disabled?0:g(a)});U.padAngle&&v&&U.padAngle(v),p&&q&&(R.append("text").attr("class","nv-pie-title"),N.select(".nv-pie-title").style("text-anchor","middle").text(function(){return q}).style("font-size",Math.min(F,G)*y*2/(q.length+2)+"px").attr("dy","0.35em").attr("transform",function(){return"translate(0, "+s+")"}));var V=N.select(".nv-pie").selectAll(".nv-slice").data(U),W=N.select(".nv-pieLabels").selectAll(".nv-label").data(U);V.exit().remove(),W.exit().remove();var X=V.enter().append("g");X.attr("class","nv-slice"),X.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),r&&d3.select(this).select("path").transition().duration(70).attr("d",C[b]),A.elementMouseover({data:a.data,index:b,color:d3.select(this).style("fill")})}),X.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),r&&d3.select(this).select("path").transition().duration(50).attr("d",B[b]),A.elementMouseout({data:a.data,index:b})}),X.on("mousemove",function(a,b){A.elementMousemove({data:a.data,index:b})}),X.on("click",function(a,b){A.elementClick({data:a.data,index:b,color:d3.select(this).style("fill")})}),X.on("dblclick",function(a,b){A.elementDblClick({data:a.data,index:b,color:d3.select(this).style("fill")})}),V.attr("fill",function(a,b){return j(a.data,b)}),V.attr("stroke",function(a,b){return j(a.data,b)});X.append("path").each(function(a){this._current=a});if(V.select("path").transition().attr("d",function(a,b){return B[b](a)}).attrTween("d",E),l){for(var Y=[],M=0;M<b[0].length;M++)Y.push(B[M]),m?p&&(Y[M]=d3.svg.arc().outerRadius(B[M].outerRadius()),u!==!1&&Y[M].startAngle(u),w!==!1&&Y[M].endAngle(w)):p||Y[M].innerRadius(0);W.enter().append("g").classed("nv-label",!0).each(function(a){var b=d3.select(this);b.attr("transform",function(a,b){if(t){a.outerRadius=I[b]+10,a.innerRadius=I[b]+15;var c=(a.startAngle+a.endAngle)/2*(180/Math.PI);return(a.startAngle+a.endAngle)/2<Math.PI?c-=90:c+=90,"translate("+Y[b].centroid(a)+") rotate("+c+")"}return a.outerRadius=H+10,a.innerRadius=H+15,"translate("+Y[b].centroid(a)+")"}),b.append("rect").style("stroke","#fff").style("fill","#fff").attr("rx",3).attr("ry",3),b.append("text").style("text-anchor",t?(a.startAngle+a.endAngle)/2<Math.PI?"start":"end":"middle").style("fill","#000")});var Z={},$=14,_=140,ab=function(a){return Math.floor(a[0]/_)*_+","+Math.floor(a[1]/$)*$};W.watchTransition(D,"pie labels").attr("transform",function(a,b){if(t){a.outerRadius=I[b]+10,a.innerRadius=I[b]+15;var c=(a.startAngle+a.endAngle)/2*(180/Math.PI);return(a.startAngle+a.endAngle)/2<Math.PI?c-=90:c+=90,"translate("+Y[b].centroid(a)+") rotate("+c+")"}a.outerRadius=H+10,a.innerRadius=H+15;var d=Y[b].centroid(a);if(a.value){var e=ab(d);Z[e]&&(d[1]-=$),Z[ab(d)]=!0}return"translate("+d+")"}),W.select(".nv-label text").style("text-anchor",function(a){return t?(a.startAngle+a.endAngle)/2<Math.PI?"start":"end":"middle"}).text(function(a,b){var c=(a.endAngle-a.startAngle)/(2*Math.PI),d="";if(!a.value||o>c)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var k=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){k(a,L.elementClick)}).on("dblclick",function(a){k(a,L.elementDblClick)}).on("mouseover",function(a){k(a,L.elementMouseover)}).on("mouseout",function(a){k(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b)
}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;f<a.length;f++)Math.abs(e.x()(a[f],f)-b)<c&&(c=Math.abs(e.x()(a[f],f)-b),d=f);return d}if(!j){var b=d3.mouse(this)[0]-f.left;i=[a(p,Math.round(c.invert(b)))],q()}}var s=d3.select(this);a.utils.initSVG(s);var t=a.utils.availableWidth(g,s,f),u=a.utils.availableHeight(h,s,f);if(b.update=function(){s.call(b)},b.container=this,!p||!p.length)return a.utils.noData(b,s),b;s.selectAll(".nv-noData").remove();var v=e.y()(p[p.length-1],p.length-1);c=e.xScale(),d=e.yScale();var w=s.selectAll("g.nv-wrap.nv-sparklineplus").data([p]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-sparklineplus"),y=x.append("g"),z=w.select("g");y.append("g").attr("class","nv-sparklineWrap"),y.append("g").attr("class","nv-valueWrap"),y.append("g").attr("class","nv-hoverArea"),w.attr("transform","translate("+f.left+","+f.top+")");var A=z.select(".nv-sparklineWrap");if(e.width(t).height(u),A.call(e),m){var B=z.select(".nv-valueWrap"),C=B.selectAll(".nv-currentValue").data([v]);C.enter().append("text").attr("class","nv-currentValue").attr("dx",o?-8:8).attr("dy",".9em").style("text-anchor",o?"end":"start"),C.attr("x",t+(o?f.right:0)).attr("y",n?function(a){return d(a)}:0).style("fill",e.color()(p[p.length-1],p.length-1)).text(l(v))}y.select(".nv-hoverArea").append("rect").on("mousemove",r).on("click",function(){j=!j}).on("mouseout",function(){i=[],q()}),z.select(".nv-hoverArea rect").attr("transform",function(){return"translate("+-f.left+","+-f.top+")"}).attr("width",t+f.left+f.right).attr("height",u+f.top)}),b}var c,d,e=a.models.sparkline(),f={top:15,right:100,bottom:10,left:50},g=null,h=null,i=[],j=!1,k=d3.format(",r"),l=d3.format(",.2f"),m=!0,n=!0,o=!1,p=null;return b.sparkline=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},xTickFormat:{get:function(){return k},set:function(a){k=a}},yTickFormat:{get:function(){return l},set:function(a){l=a}},showLastValue:{get:function(){return m},set:function(a){m=a}},alignValue:{get:function(){return n},set:function(a){n=a}},rightAlignValue:{get:function(){return o},set:function(a){o=a}},noData:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.stackedArea=function(){"use strict";function b(m){return u.reset(),u.models(r),m.each(function(m){var s=f-e.left-e.right,v=g-e.top-e.bottom;j=d3.select(this),a.utils.initSVG(j),c=r.xScale(),d=r.yScale();var w=m;m.forEach(function(a,b){a.seriesIndex=b,a.values=a.values.map(function(a,c){return a.index=c,a.seriesIndex=b,a})});var x=m.filter(function(a){return!a.disabled});m=d3.layout.stack().order(o).offset(n).values(function(a){return a.values}).x(k).y(l).out(function(a,b,c){a.display={y:c,y0:b}})(x);var y=j.selectAll("g.nv-wrap.nv-stackedarea").data([m]),z=y.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedarea"),A=z.append("defs"),B=z.append("g"),C=y.select("g");B.append("g").attr("class","nv-areaWrap"),B.append("g").attr("class","nv-scatterWrap"),y.attr("transform","translate("+e.left+","+e.top+")"),0==r.forceY().length&&r.forceY().push(0),r.width(s).height(v).x(k).y(function(a){return a.display.y+a.display.y0}).forceY([0]).color(m.map(function(a){return a.color||h(a,a.seriesIndex)}));var D=C.select(".nv-scatterWrap").datum(m);D.call(r),A.append("clipPath").attr("id","nv-edge-clip-"+i).append("rect"),y.select("#nv-edge-clip-"+i+" rect").attr("width",s).attr("height",v),C.attr("clip-path",q?"url(#nv-edge-clip-"+i+")":"");var E=d3.svg.area().x(function(a,b){return c(k(a,b))}).y0(function(a){return d(a.display.y0)}).y1(function(a){return d(a.display.y+a.display.y0)}).interpolate(p),F=d3.svg.area().x(function(a,b){return c(k(a,b))}).y0(function(a){return d(a.display.y0)}).y1(function(a){return d(a.display.y0)}),G=C.select(".nv-areaWrap").selectAll("path.nv-area").data(function(a){return a});G.enter().append("path").attr("class",function(a,b){return"nv-area nv-area-"+b}).attr("d",function(a){return F(a.values,a.seriesIndex)}).on("mouseover",function(a){d3.select(this).classed("hover",!0),t.areaMouseover({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}).on("mouseout",function(a){d3.select(this).classed("hover",!1),t.areaMouseout({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}).on("click",function(a){d3.select(this).classed("hover",!1),t.areaClick({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}),G.exit().remove(),G.style("fill",function(a){return a.color||h(a,a.seriesIndex)}).style("stroke",function(a){return a.color||h(a,a.seriesIndex)}),G.watchTransition(u,"stackedArea path").attr("d",function(a,b){return E(a.values,b)}),r.dispatch.on("elementMouseover.area",function(a){C.select(".nv-chart-"+i+" .nv-area-"+a.seriesIndex).classed("hover",!0)}),r.dispatch.on("elementMouseout.area",function(a){C.select(".nv-chart-"+i+" .nv-area-"+a.seriesIndex).classed("hover",!1)}),b.d3_stackedOffset_stackPercent=function(a){var b,c,d,e=a.length,f=a[0].length,g=[];for(c=0;f>c;++c){for(b=0,d=0;b<w.length;b++)d+=l(w[b].values[c]);if(d)for(b=0;e>b;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}();!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++i<u;)(t=r[i].on)&&t.apply(this,arguments);return n}var e=[],r=new c;return t.on=function(t,i){var u,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,u=e.indexOf(o)).concat(e.slice(u+1)),r.remove(t)),i&&e.push(r.set(t,{on:i})),n)},t}function S(){ao.event.preventDefault()}function k(){for(var n,t=ao.event;n=t.sourceEvent;)t=n;return t}function N(n){for(var t=new _,e=0,r=arguments.length;++e<r;)t[arguments[e]]=w(t);return t.of=function(e,r){return function(i){try{var u=i.sourceEvent=ao.event;i.target=n,ao.event=i,t[i.type].apply(e,r)}finally{ao.event=u}}},t}function E(n){return ko(n,Co),n}function A(n){return"function"==typeof n?n:function(){return No(n,this)}}function C(n){return"function"==typeof n?n:function(){return Eo(n,this)}}function z(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=ao.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?u:i}function L(n){return n.trim().replace(/\s+/g," ")}function q(n){return new RegExp("(?:^|\\s+)"+ao.requote(n)+"(?:\\s+|$)","g")}function T(n){return(n+"").trim().split(/^|\s+/)}function R(n,t){function e(){for(var e=-1;++e<i;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<i;)n[e](this,r)}n=T(n).map(D);var i=n.length;return"function"==typeof t?r:e}function D(n){var t=q(n);return function(e,r){if(i=e.classList)return r?i.add(n):i.remove(n);var i=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(i)||e.setAttribute("class",L(i+" "+n))):e.setAttribute("class",L(i.replace(t," ")))}}function P(n,t,e){function r(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,e)}function u(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?u:i}function U(n,t){function e(){delete this[n]}function r(){this[n]=t}function i(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?i:r}function j(n){function t(){var t=this.ownerDocument,e=this.namespaceURI;return e===zo&&t.documentElement.namespaceURI===zo?t.createElement(n):t.createElementNS(e,n)}function e(){return this.ownerDocument.createElementNS(n.space,n.local)}return"function"==typeof n?n:(n=ao.ns.qualify(n)).local?e:t}function F(){var n=this.parentNode;n&&n.removeChild(this)}function H(n){return{__data__:n}}function O(n){return function(){return Ao(this,n)}}function I(n){return arguments.length||(n=e),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function Y(n,t){for(var e=0,r=n.length;r>e;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t<l;);return o}}function X(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function i(){var i=l(t,co(arguments));r.call(this),this.addEventListener(n,this[o]=i,i.$=e),i._=t}function u(){var t,e=new RegExp("^__on([^.]+)"+ao.requote(n)+"$");for(var r in this)if(t=r.match(e)){var i=this[r];this.removeEventListener(t[1],i,i.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),l=$;a>0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t<e&&(e=t.t),t=(n=t).n):t=n?n.n=t.n:oa=t.n;return aa=n,e}function Pn(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Un(n,t){var e=Math.pow(10,3*xo(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.slice(l,a)),null!=(i=ya[e=n.charAt(++a)])&&(e=n.charAt(++a)),(u=A[e])&&(e=u(t,null==i?"e"===e?" ":"0":i)),o.push(e),l=a+1);return o.push(n.slice(l,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},i=e(r,n,t,0);if(i!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var u=null!=r.Z&&va!==Hn,o=new(u?Hn:va);return"j"in r?o.setFullYear(r.y,0,r.j):"W"in r||"U"in r?("w"in r||(r.w="W"in r?1:0),o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+(r.Z/100|0),r.M+r.Z%100,r.S,r.L),u?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var i,u,o,a=0,l=t.length,c=e.length;l>a;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function $n(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Bn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.U=+r[0],e+r[0].length):-1}function Wn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.W=+r[0],e+r[0].length):-1}function Jn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Gn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.y=Qn(+r[0]),e+r[0].length):-1}function Kn(n,t,e){return/^[+-]\d{4}$/.test(t=t.slice(e,e+5))?(n.Z=-t,e+5):-1}function Qn(n){return n+(n>68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function ft(){}function st(n,t,e){var r=e.s=n+t,i=r-n,u=r-i;e.t=n-u+(t-i)}function ht(n,t){n&&wa.hasOwnProperty(n.type)&&wa[n.type](n,t)}function pt(n,t,e){var r,i=-1,u=n.length-e;for(t.lineStart();++i<u;)r=n[i],t.point(r[0],r[1],r[2]);t.lineEnd()}function gt(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)pt(n[e],t,1);t.polygonEnd()}function vt(){function n(n,t){n*=Yo,t=t*Yo/2+Fo/4;var e=n-r,o=e>=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])<Uo&&xo(n[1]-t[1])<Uo}function St(n,t){n*=Yo;var e=Math.cos(t*=Yo);kt(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function kt(n,t,e){++Ea,Ca+=(n-Ca)/Ea,za+=(t-za)/Ea,La+=(e-La)/Ea}function Nt(){function n(n,i){n*=Yo;var u=Math.cos(i*=Yo),o=u*Math.cos(n),a=u*Math.sin(n),l=Math.sin(i),c=Math.atan2(Math.sqrt((c=e*l-r*a)*c+(c=r*o-t*l)*c+(c=t*a-e*o)*c),t*o+e*a+r*l);Aa+=c,qa+=c*(t+(t=o)),Ta+=c*(e+(e=a)),Ra+=c*(r+(r=l)),kt(t,e,r)}var t,e,r;ja.point=function(i,u){i*=Yo;var o=Math.cos(u*=Yo);t=o*Math.cos(i),e=o*Math.sin(i),r=Math.sin(u),ja.point=n,kt(t,e,r)}}function Et(){ja.point=St}function At(){function n(n,t){n*=Yo;var e=Math.cos(t*=Yo),o=e*Math.cos(n),a=e*Math.sin(n),l=Math.sin(t),c=i*l-u*a,f=u*o-r*l,s=r*a-i*o,h=Math.sqrt(c*c+f*f+s*s),p=r*o+i*a+u*l,g=h&&-nn(p)/h,v=Math.atan2(h,p);Da+=g*c,Pa+=g*f,Ua+=g*s,Aa+=v,qa+=v*(r+(r=o)),Ta+=v*(i+(i=a)),Ra+=v*(u+(u=l)),kt(r,i,u)}var t,e,r,i,u;ja.point=function(o,a){t=o,e=a,ja.point=n,o*=Yo;var l=Math.cos(a*=Yo);r=l*Math.cos(o),i=l*Math.sin(o),u=Math.sin(a),kt(r,i,u)},ja.lineEnd=function(){n(t,e),ja.lineEnd=Et,ja.point=St}}function Ct(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function zt(){return!0}function Lt(n,t,e,r,i){var u=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(wt(e,r)){i.lineStart();for(var a=0;t>a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r<t;)i.n=e=n[r],e.p=i,i=e;i.n=e=n[0],e.p=i}}function Tt(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Rt(n,t,e,r){return function(i,u){function o(t,e){var r=i(t,e);n(t=r[0],e=r[1])&&u.point(t,e)}function a(n,t){var e=i(n,t);d.point(e[0],e[1])}function l(){m.point=a,d.lineStart()}function c(){m.point=o,d.lineEnd()}function f(n,t){v.push([n,t]);var e=i(n,t);x.point(e[0],e[1])}function s(){x.lineStart(),v=[]}function h(){f(v[0][0],v[0][1]),x.lineEnd();var n,t=x.clean(),e=M.buffer(),r=e.length;if(v.pop(),g.push(v),v=null,r)if(1&t){n=e[0];var i,r=n.length-1,o=-1;if(r>0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o<r;)u.point((i=n[o])[0],i[1]);u.lineEnd()}}else r>1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)<Uo?(n.point(e,r=(r+o)/2>0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)<Uo&&(e-=i*Uo),xo(u-a)<Uo&&(u-=a*Uo),r=Ft(e,r,u,o),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=u,r=o),i=a},lineEnd:function(){n.lineEnd(),e=r=NaN},clean:function(){return 2-t}}}function Ft(n,t,e,r){var i,u,o=Math.sin(n-e);return xo(o)>Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]<t[0]?Fo:-Fo;i=e*u/2,r.point(-u,i),r.point(0,i),r.point(u,i)}else r.point(t[0],t[1])}function Ot(n,t){var e=n[0],r=n[1],i=[Math.sin(e),-Math.cos(e),0],u=0,o=0;ka.reset();for(var a=0,l=t.length;l>a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)<Uo,C=A||Uo>E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)<Uo?k:N):k<=b[1]&&b[1]<=N:E>Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)<Uo?i>0?0:3:xo(r[0]-e)<Uo?i>0?2:1:xo(r[1]-t)<Uo?i>0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){
r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)<Uo||xo(r-h)<Uo?(r+h)/2:Math.atan2(_,b),E=n(N,k),A=E[0],C=E[1],z=A-t,L=C-e,q=M*z-m*L;(q*q/x>u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)<Uo?ce:(e.invert=function(n,t){var e=u-t;return[Math.atan2(n,e)/i,u-K(i)*Math.sqrt(n*n+e*e)]},e)}function Ne(n,t){return[n,Math.log(Math.tan(Fo/4+t/2))]}function Ee(n){var t,e=oe(n),r=e.scale,i=e.translate,u=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=i.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=u.apply(e,arguments);if(o===e){if(t=null==n){var a=Fo*r(),l=i();u([[l[0]-a,l[1]-a],[l[0]+a,l[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Ae(n,t){return[Math.log(Math.tan(Fo/4+t/2)),-n]}function Ce(n){return n[0]}function ze(n){return n[1]}function Le(n){for(var t=n.length,e=[0,1],r=2,i=2;t>i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)<Uo&&xo(r-l.circle.cy)<Uo;)u=l.P,a.unshift(l),je(l),l=u;a.unshift(l),Be(l);for(var c=o;c.circle&&xo(e-c.circle.x)<Uo&&xo(r-c.circle.cy)<Uo;)o=c.N,a.push(c),je(c),c=o;a.push(c),Be(c);var f,s=a.length;for(f=1;s>f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)<Uo&&g-i>Uo?{x:s,y:xo(t-s)<Uo?e:g}:xo(i-g)<Uo&&h-r>Uo?{x:xo(e-g)<Uo?t:h,y:g}:xo(r-h)<Uo&&i-p>Uo?{x:h,y:xo(t-h)<Uo?e:p}:xo(i-p)<Uo&&r-s>Uo?{x:xo(e-p)<Uo?t:s,y:p}:null),u.site,null)),++l)}function Ve(n,t){return t.angle-n.angle}function Xe(){rr(this),this.x=this.y=this.arc=this.site=this.cy=null}function $e(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,i=n.site,u=e.site;if(r!==u){var o=i.x,a=i.y,l=r.x-o,c=r.y-a,f=u.x-o,s=u.y-a,h=2*(l*s-c*f);if(!(h>=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.y<M.y||y.y===M.y&&y.x<=M.x){if(!M.L){m=M.P;break}M=M.L}else{if(!M.R){m=M;break}M=M.R}ll.insert(m,y),m||(al=y)}}}}function Be(n){var t=n.circle;t&&(t.P||(al=t.N),ll.remove(t),fl.push(t),rr(t),n.circle=null)}function We(n){for(var t,e=il,r=Yt(n[0][0],n[0][1],n[1][0],n[1][1]),i=e.length;i--;)t=e[i],(!Je(t,n)||!r(t)||xo(t.a.x-t.b.x)<Uo&&xo(t.a.y-t.b.y)<Uo)&&(t.a=t.b=null,e.splice(i,1))}function Je(n,t){var e=n.b;if(e)return!0;var r,i,u=n.a,o=t[0][0],a=t[1][0],l=t[0][1],c=t[1][1],f=n.l,s=n.r,h=f.x,p=f.y,g=s.x,v=s.y,d=(h+g)/2,y=(p+v)/2;if(v===p){if(o>d||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.y<l)return}else u={x:d,y:c};e={x:d,y:l}}}else if(r=(h-g)/(v-p),i=y-r*d,-1>r||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.y<l)return}else u={x:(c-i)/r,y:c};e={x:(l-i)/r,y:l}}else if(v>p){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.x<o)return}else u={x:a,y:r*a+i};e={x:o,y:r*o+i}}return n.a=u,n.b=e,!0}function Ge(n,t){this.l=n,this.r=t,this.a=this.b=null}function Ke(n,t,e,r){var i=new Ge(n,t);return il.push(i),e&&nr(i,n,t,e),r&&nr(i,t,n,r),ul[n.i].edges.push(new tr(i,n,t)),ul[t.i].edges.push(new tr(i,t,n)),i}function Qe(n,t,e){var r=new Ge(n,null);return r.a=t,r.b=e,il.push(r),r}function nr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function tr(n,t,e){var r=n.a,i=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(i.x-r.x,r.y-i.y):Math.atan2(r.x-i.x,i.y-r.y)}function er(){this._=null}function rr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function ir(n,t){var e=t,r=t.R,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function ur(n,t){var e=t,r=t.L,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function or(n){for(;n.L;)n=n.L;return n}function ar(n,t){var e,r,i,u=n.sort(lr).pop();for(il=[],ul=new Array(n.length),ol=new er,ll=new er;;)if(i=al,u&&(!i||u.y<i.y||u.y===i.y&&u.x<i.x))u.x===e&&u.y===r||(ul[u.i]=new Ye(u),He(u),e=u.x,r=u.y),u=n.pop();else{if(!i)break;Fe(i.arc)}t&&(We(t),Ze(t));var o={cells:ul,edges:il};return ol=ll=il=ul=null,o}function lr(n,t){return t.y-n.y||t.x-n.x}function cr(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function fr(n){return n.x}function sr(n){return n.y}function hr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function pr(n,t,e,r,i,u){if(!n(t,e,r,i,u)){var o=.5*(e+i),a=.5*(r+u),l=t.nodes;l[0]&&pr(n,l[0],e,r,o,a),l[1]&&pr(n,l[1],o,r,i,a),l[2]&&pr(n,l[2],e,a,o,u),l[3]&&pr(n,l[3],o,a,i,u)}}function gr(n,t,e,r,i,u,o){var a,l=1/0;return function c(n,f,s,h,p){if(!(f>u||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return u<t.length&&(i=t.slice(u),a[o]?a[o]+=i:a[++o]=i),a.length<2?l[0]?(t=l[0].x,function(n){return t(n)+""}):function(){return t}:(t=l.length,function(n){for(var e,r=0;t>r;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Zo,this.translate=[n.e,n.f],this.scale=[r,u],this.skew=u?Math.atan2(i,u)*Zo:0}function Fr(n,t){return n[0]*t[0]+n[1]*t[1]}function Hr(n){var t=Math.sqrt(Fr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function Or(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ir(n){return n.length?n.pop()+",":""}function Yr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push("translate(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else(t[0]||t[1])&&e.push("translate("+t+")")}function Zr(n,t,e,r){n!==t?(n-t>180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i<u;)e[(t=r[i]).i]=t.x(n);return e.join("")}}function Br(n,t){return t=(t-=n=+n)||1/t,function(e){return(e-n)/t}}function Wr(n,t){return t=(t-=n=+n)||1/t,function(e){return Math.max(0,Math.min(1,(e-n)/t))}}function Jr(n){for(var t=n.source,e=n.target,r=Kr(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(var u=i.length;e!==r;)i.splice(u,0,e),e=e.parent;return i}function Gr(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Kr(n,t){if(n===t)return n;for(var e=Gr(n),r=Gr(t),i=e.pop(),u=r.pop(),o=null;i===u;)o=i,i=e.pop(),u=r.pop();return o}function Qr(n){n.fixed|=2}function ni(n){n.fixed&=-7}function ti(n){n.fixed|=4,n.px=n.x,n.py=n.y}function ei(n){n.fixed&=-5}function ri(n,t,e){var r=0,i=0;if(n.charge=0,!n.leaf)for(var u,o=n.nodes,a=o.length,l=-1;++l<a;)u=o[l],null!=u&&(ri(u,t,e),n.charge+=u.charge,r+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var c=t*e[n.point.index];n.charge+=n.pointCharge=c,r+=c*n.point.x,i+=c*n.point.y}n.cx=r/n.charge,n.cy=i/n.charge}function ii(n,t){return ao.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=fi,n}function ui(n,t){for(var e=[n];null!=(n=e.pop());)if(t(n),(i=n.children)&&(r=i.length))for(var r,i;--r>=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++o<i;)e.push(u[o]);for(;null!=(n=r.pop());)t(n)}function ai(n){return n.children}function li(n){return n.value}function ci(n,t){return t.value-n.value}function fi(n){return ao.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function si(n){return n.x}function hi(n){return n.y}function pi(n,t,e){n.y0=t,n.y=e}function gi(n){return ao.range(n.length)}function vi(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function di(n){for(var t,e=1,r=0,i=n[0][1],u=n.length;u>e;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.r<r.r?Si(r,i=a):Si(r=l,i),o--):(wi(r,u),i=u,t(u))}var y=(f+s)/2,m=(h+p)/2,M=0;for(o=0;c>o;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u<o;)Ci(i[u],t,e,r)}function zi(n,t,e){var r=n.r+e.r,i=t.x-n.x,u=t.y-n.y;if(r&&(i||u)){var o=t.r+e.r,a=i*i+u*u;o*=o,r*=r;var l=.5+(r-o)/(2*a),c=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+l*i+c*u,e.y=n.y+l*u-c*i}else e.x=n.x+r,e.y=n.y}function Li(n,t){return n.parent==t.parent?1:2}function qi(n){var t=n.children;return t.length?t[0]:n.t}function Ti(n){var t,e=n.children;return(t=e.length)?e[t-1]:n.t}function Ri(n,t,e){var r=e/(t.i-n.i);t.c-=r,t.s+=e,n.c+=r,t.z+=e,t.m+=e}function Di(n){for(var t,e=0,r=0,i=n.children,u=i.length;--u>=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)i.push(e(n[o-1],n[o])),u.push(r(t[o-1],t[o]));return function(t){var e=ao.bisect(n,t,1,a)-1;return u[e](i[e](t))}}function Wi(n,t,e,r){function i(){var i=Math.min(n.length,t.length)>2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++<f;)for(var h=s-1;h>0;h--)o.push(u(c)*h);for(c=0;o[c]<a;c++);for(f=o.length;o[f-1]>l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++o<a;)i.has(u=r[o])||i.set(u,n.push(u));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(u=n,o=0,t={t:"range",a:arguments},e):u},e.rangePoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=(l+c)/2,0):(c-l)/(n.length-1+a);return u=r(l+f*a/2,f),o=0,t={t:"rangePoints",a:arguments},e},e.rangeRoundPoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=c=Math.round((l+c)/2),0):(c-l)/(n.length-1+a)|0;return u=r(l+Math.round(f*a/2+(c-l-(n.length-1+a)*f)/2),f),o=0,t={t:"rangeRoundPoints",a:arguments},e},e.rangeBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=(s-f)/(n.length-a+2*l);return u=r(f+h*l,h),c&&u.reverse(),o=h*(1-a),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=Math.floor((s-f)/(n.length-a+2*l));return u=r(f+Math.round((s-f-(n.length-a)*h)/2),h),c&&u.reverse(),o=Math.round(h*(1-a)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return o},e.rangeExtent=function(){return Yi(t.a[0])},e.copy=function(){return ou(n,t)},e.domain(n)}function au(n,t){function u(){var e=0,r=t.length;for(a=[];++e<r;)a[e-1]=ao.quantile(n,e/r);return o}function o(n){return isNaN(n=+n)?void 0:t[ao.bisect(a,n)]}var a;return o.domain=function(t){return arguments.length?(n=t.map(r).filter(i).sort(e),u()):n},o.range=function(n){return arguments.length?(t=n,u()):t},o.quantiles=function(){return a},o.invertExtent=function(e){return e=t.indexOf(e),0>e?[NaN,NaN]:[e>0?a[e-1]:n[0],e<a.length?a[e]:n[n.length-1]]},o.copy=function(){return au(n,t)},u()}function lu(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(u*(t-n))))]}function i(){return u=e.length/(t-n),o=e.length-1,r}var u,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],i()):[n,t]},r.range=function(n){return arguments.length?(e=n,i()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s<h;)i.call(this,l=t[s],s)?f.push([+p.call(this,l,s),+g.call(this,l,s)]):f.length&&(o(),f=[]);return f.length&&o(),c.length?c.join(""):null}var e=Ce,r=ze,i=zt,u=xu,o=u.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(i=n,t):i},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?u=n:(u=Tl.get(n)||xu).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function xu(n){return n.length>1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("V",(r=n[t])[1],"H",r[0]);return i.join("")}function Su(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r=n[t])[0],"V",r[1]);return i.join("")}function ku(n,t){return n.length<4?xu(n):n[1]+Au(n.slice(1,-1),Cu(n,t))}function Nu(n,t){return n.length<3?bu(n):n[0]+Au((n.push(n[0]),n),Cu([n[n.length-2]].concat(n,[n[1]]),t))}function Eu(n,t){return n.length<3?xu(n):n[0]+Au(n,Cu(n,t))}function Au(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return xu(n);var e=n.length!=t.length,r="",i=n[0],u=n[1],o=t[0],a=o,l=1;if(e&&(r+="Q"+(u[0]-2*o[0]/3)+","+(u[1]-2*o[1]/3)+","+u[0]+","+u[1],i=n[1],l=2),t.length>1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c<t.length;c++,l++)u=n[l],a=t[c],r+="S"+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1]}if(e){var f=n[l];r+="Q"+(u[0]+2*a[0]/3)+","+(u[1]+2*a[1]/3)+","+f[0]+","+f[1]}return r}function Cu(n,t){for(var e,r=[],i=(1-t)/2,u=n[0],o=n[1],a=1,l=n.length;++a<l;)e=u,u=o,o=n[a],r.push([i*(o[0]-e[0]),i*(o[1]-e[1])]);return r}function zu(n){if(n.length<3)return xu(n);var t=1,e=n.length,r=n[0],i=r[0],u=r[1],o=[i,i,i,(r=n[1])[0]],a=[u,u,u,r[1]],l=[i,",",u,"L",Ru(Pl,o),",",Ru(Pl,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Du(l,o,a);return n.pop(),l.push("L",r),l.join("")}function Lu(n){if(n.length<4)return xu(n);for(var t,e=[],r=-1,i=n.length,u=[0],o=[0];++r<3;)t=n[r],u.push(t[0]),o.push(t[1]);for(e.push(Ru(Pl,u)+","+Ru(Pl,o)),--r;++r<i;)t=n[r],u.shift(),u.push(t[0]),o.shift(),o.push(t[1]),Du(e,u,o);return e.join("")}function qu(n){for(var t,e,r=-1,i=n.length,u=i+4,o=[],a=[];++r<4;)e=n[r%i],o.push(e[0]),a.push(e[1]);for(t=[Ru(Pl,o),",",Ru(Pl,a)],--r;++r<u;)e=n[r%i],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Du(t,o,a);return t.join("")}function Tu(n,t){var e=n.length-1;if(e)for(var r,i,u=n[0][0],o=n[0][1],a=n[e][0]-u,l=n[e][1]-o,c=-1;++c<=e;)r=n[c],i=c/e,r[0]=t*r[0]+(1-t)*(u+i*a),r[1]=t*r[1]+(1-t)*(o+i*l);return zu(n)}function Ru(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Du(n,t,e){n.push("C",Ru(Rl,t),",",Ru(Rl,e),",",Ru(Dl,t),",",Ru(Dl,e),",",Ru(Pl,t),",",Ru(Pl,e))}function Pu(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Uu(n){for(var t=0,e=n.length-1,r=[],i=n[0],u=n[1],o=r[0]=Pu(i,u);++t<e;)r[t]=(o+(o=Pu(i=u,u=n[t+1])))/2;return r[t]=o,r}function ju(n){for(var t,e,r,i,u=[],o=Uu(n),a=-1,l=n.length-1;++a<l;)t=Pu(n[a],n[a+1]),xo(t)<Uo?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,i=e*e+r*r,i>9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i<u;)t=n[i],e=t[0],r=t[1]-Io,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Ou(n){function t(t){function l(){v.push("M",a(n(y),s),f,c(n(d.reverse()),s),"Z")}for(var h,p,g,v=[],d=[],y=[],m=-1,M=t.length,x=En(e),b=En(i),_=e===r?function(){
return p}:En(r),w=i===u?function(){return g}:En(u);++m<M;)o.call(this,h=t[m],m)?(d.push([p=+x.call(this,h,m),g=+b.call(this,h,m)]),y.push([+_.call(this,h,m),+w.call(this,h,m)])):d.length&&(l(),d=[],y=[]);return d.length&&l(),v.length?v.join(""):null}var e=Ce,r=Ce,i=0,u=ze,o=zt,a=xu,l=a.key,c=a,f="L",s=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(i=u=n,t):u},t.y0=function(n){return arguments.length?(i=n,t):i},t.y1=function(n){return arguments.length?(u=n,t):u},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(l="function"==typeof n?a=n:(a=Tl.get(n)||xu).key,c=a.reverse||a,f=a.closed?"M":"L",t):l},t.tension=function(n){return arguments.length?(s=n,t):s},t}function Iu(n){return n.radius}function Yu(n){return[n.x,n.y]}function Zu(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]-Io;return[e*Math.cos(r),e*Math.sin(r)]}}function Vu(){return 64}function Xu(){return"circle"}function $u(n){var t=Math.sqrt(n/Fo);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Bu(n){return function(){var t,e,r;(t=this[n])&&(r=t[e=t.active])&&(r.timer.c=null,r.timer.t=NaN,--t.count?delete t[e]:delete this[n],t.active+=.5,r.event&&r.event.interrupt.call(this,this.__data__,r.index))}}function Wu(n,t,e){return ko(n,Yl),n.namespace=t,n.id=e,n}function Ju(n,t,e,r){var i=n.id,u=n.namespace;return Y(n,"function"==typeof e?function(n,o,a){n[u][i].tween.set(t,r(e.call(n,n.__data__,o,a)))}:(e=r(e),function(n){n[u][i].tween.set(t,e)}))}function Gu(n){return null==n&&(n=""),function(){this.textContent=n}}function Ku(n){return null==n?"__transition__":"__transition_"+n+"__"}function Qu(n,t,e,r,i){function u(n){var t=v.delay;return f.t=t+l,n>=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]<Kl[u]/i?u-1:u]:[tc,Ki(n,e)[2]]}return r.invert=function(t){return io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,io(+e+1),t).length}var u=r.domain(),o=Yi(u),a=null==n?i(o,10):"number"==typeof n&&i(o,n);return a&&(n=a[0],t=a[1]),r.domain(Xi(u,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&e>r&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&e>r&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&r>e&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&r>e&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u<o;)if(null!=(r=n[u])&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=n[u])&&(e>r&&(e=r),r>i&&(i=r))}else{for(;++u<o;)if(null!=(r=t.call(n,n[u],u))&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=t.call(n,n[u],u))&&(e>r&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o<u;)i(e=+n[o])&&(r+=e);else for(;++o<u;)i(e=+t.call(n,n[o],o))&&(r+=e);return r},ao.mean=function(n,t){var e,u=0,o=n.length,a=-1,l=o;if(1===arguments.length)for(;++a<o;)i(e=r(n[a]))?u+=e:--l;else for(;++a<o;)i(e=r(t.call(n,n[a],a)))?u+=e:--l;return l?u/l:void 0},ao.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),i=+n[r-1],u=e-r;return u?i+u*(n[r]-i):i},ao.median=function(n,t){var u,o=[],a=n.length,l=-1;if(1===arguments.length)for(;++l<a;)i(u=r(n[l]))&&o.push(u);else for(;++l<a;)i(u=r(t.call(n,n[l],l)))&&o.push(u);return o.length?ao.quantile(o.sort(e),.5):void 0},ao.variance=function(n,t){var e,u,o=n.length,a=0,l=0,c=-1,f=0;if(1===arguments.length)for(;++c<o;)i(e=r(n[c]))&&(u=e-a,a+=u/++f,l+=u*(e-a));else for(;++c<o;)i(e=r(t.call(n,n[c],c)))&&(u=e-a,a+=u/++f,l+=u*(e-a));return f>1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t<e;)for(var i,u=-1,a=r[t]=new Array(i);++u<i;)a[u]=n[u][t];return r},ao.zip=function(){return ao.transpose(arguments)},ao.keys=function(n){var t=[];for(var e in n)t.push(e);return t},ao.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},ao.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},ao.merge=function(n){for(var t,e,r,i=n.length,u=-1,o=0;++u<i;)o+=n[u].length;for(e=new Array(o);--i>=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)<t;)i.push(r/u);return i},ao.map=function(n,t){var e=new c;if(n instanceof c)n.forEach(function(n,t){e.set(n,t)});else if(Array.isArray(n)){var r,i=-1,u=n.length;if(1===arguments.length)for(;++i<u;)e.set(i,n[i]);else for(;++i<u;)e.set(t.call(n,r=n[i],i),r)}else for(var o in n)e.set(o,n[o]);return e};var bo="__proto__",_o="\x00";l(c,{has:h,get:function(n){return this._[f(n)]},set:function(n,t){return this._[f(n)]=t},remove:p,keys:g,values:function(){var n=[];for(var t in this._)n.push(this._[t]);return n},entries:function(){var n=[];for(var t in this._)n.push({key:s(t),value:this._[t]});return n},size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t),this._[t])}}),ao.nest=function(){function n(t,o,a){if(a>=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p<g;)(h=d.get(l=v(f=o[p])))?h.push(f):d.set(l,[f]);return t?(f=t(),s=function(e,r){f.set(e,n(t,r,a))}):(f={},s=function(e,r){f[e]=n(t,r,a)}),d.forEach(s),f}function t(n,e){if(e>=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r<i;)n[e=arguments[r]]=M(n,t,t[e]);return n};var wo=["webkit","ms","moz","Moz","o","O"];ao.dispatch=function(){for(var n=new _,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=w(n);return n},_.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o<a;){u.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var l=-1,c=r.length;++l<c;)(i=r[l])?(t.push(e=n.call(i,i.__data__,l,o)),e&&"__data__"in i&&(e.__data__=i.__data__)):t.push(null)}return E(u)},Co.selectAll=function(n){var t,e,r=[];n=C(n);for(var i=-1,u=this.length;++i<u;)for(var o=this[i],a=-1,l=o.length;++a<l;)(e=o[a])&&(r.push(t=co(n.call(e,e.__data__,a,i))),t.parentNode=e);return E(r)};var zo="http://www.w3.org/1999/xhtml",Lo={svg:"http://www.w3.org/2000/svg",xhtml:zo,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};ao.ns={prefix:Lo,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++i<r;)if(!t.contains(n[i]))return!1}else for(t=e.getAttribute("class");++i<r;)if(!q(n[i]).test(t))return!1;return!0}for(t in n)this.each(R(t,n[t]));return this}return this.each(R(n,t))},Co.style=function(n,e,r){var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++r<o;)(i=n[r])&&(y.has(d=t.call(i,i.__data__,r))?v[r]=i:y.set(d,i),m[r]=d);for(r=-1;++r<s;)(i=y.get(d=t.call(e,u=e[r],r)))?i!==!0&&(p[r]=i,i.__data__=u):g[r]=H(u),y.set(d,!0);for(r=-1;++r<o;)r in m&&y.get(m[r])!==!0&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],u=e[r],i?(i.__data__=u,p[r]=i):g[r]=H(u);for(;s>r;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++u<o;)(i=r[u])&&(n[u]=i.__data__);return n}var a=Z([]),l=E([]),f=E([]);if("function"==typeof n)for(;++u<o;)e(r=this[u],n.call(r,r.parentNode.__data__,u));else for(;++u<o;)e(r=this[u],n);return l.enter=function(){return a},l.exit=function(){return f},l},Co.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},Co.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],i=r.length-1,u=r[i];--i>=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},Co.each=function(n){return Y(this,function(t,e,r){n.call(t,t.__data__,e,r)})},Co.call=function(n){var t=co(arguments);return n.apply(t[0]=this,t),this},Co.empty=function(){return!this.node()},Co.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++a<l;){r=(i=this[a]).update,o.push(t=[]),t.parentNode=i.parentNode;for(var c=-1,f=i.length;++c<f;)(u=i[c])?(t.push(r[c]=e=n.call(i.parentNode,u.__data__,c,a)),e.__data__=u.__data__):t.push(null)}return E(o)},qo.insert=function(n,t){return arguments.length<2&&(t=V(this)),Co.insert.call(this,n,t)},ao.select=function(t){var e;return"string"==typeof t?(e=[No(t,fo)],e.parentNode=fo.documentElement):(e=[t],e.parentNode=n(t)),E([e])},ao.selectAll=function(n){var t;return"string"==typeof n?(t=co(Eo(n,fo)),t.parentNode=fo.documentElement):(t=co(n),t.parentNode=null),E([t])},Co.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++<c;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}f=e+2;var r=n.charCodeAt(e+1);return 13===r?(i=!0,10===n.charCodeAt(e+2)&&++f):10===r&&(i=!0),n.slice(t+1,e).replace(/""/g,'"')}for(;c>f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++r<i;)ht(e[r].geometry,t)}},wa={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){pt(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)pt(e[r],t,0)},Polygon:function(n,t){gt(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)gt(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,i=e.length;++r<i;)ht(e[r],t)}};ao.geo.area=function(n){return Sa=0,ao.geo.stream(n,Na),Sa};var Sa,ka=new ft,Na={sphere:function(){Sa+=4*Fo},point:b,lineStart:b,lineEnd:b,polygonStart:function(){ka.reset(),Na.lineStart=vt},polygonEnd:function(){var n=2*ka;Sa+=0>n?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var f,s,h,p,g,v,d,y,m,M,x,b={point:n,lineStart:e,lineEnd:r,polygonStart:function(){b.point=i,b.lineStart=u,b.lineEnd=o,m=0,Na.polygonStart()},polygonEnd:function(){Na.polygonEnd(),b.point=n,b.lineStart=e,b.lineEnd=r,0>ka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t<f.length-h;++t)p.push(n[a[f[t]][2]]);return p}var e=Ce,r=ze;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},ao.geom.polygon=function(n){return ko(n,rl),n};var rl=ao.geom.polygon.prototype=[];rl.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],i=0;++t<e;)n=r,r=this[t],i+=n[1]*r[0]-n[0]*r[1];return.5*i},rl.centroid=function(n){var t,e,r=-1,i=this.length,u=0,o=0,a=this[i-1];for(arguments.length||(n=-1/(6*this.area()));++r<i;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],u+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[u*n,o*n]},rl.clip=function(n){for(var t,e,r,i,u,o,a=De(n),l=-1,c=this.length-De(this),f=this[c-1];++l<c;){for(t=n.slice(),n.length=0,i=this[l],u=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Te(o,f,i)?(Te(u,f,i)||n.push(Re(u,o,f,i)),n.push(o)):Te(u,f,i)&&n.push(Re(u,o,f,i)),u=o;a&&n.push(n[0]),f=i}return n};var il,ul,ol,al,ll,cl=[],fl=[];Ye.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(Ve),t.length},tr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},er.prototype={insert:function(n,t){var e,r,i;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=or(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(i=r.R,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.R&&(ir(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ur(this,r))):(i=r.L,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.L&&(ur(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ir(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,i=n.U,u=n.L,o=n.R;if(e=u?o?or(o):u:o,i?i.L===n?i.L=e:i.R=e:this._=e,u&&o?(r=e.C,e.C=n.C,e.L=u,u.U=e,e!==o?(i=e.U,e.U=n.U,n=e.R,i.L=n,e.R=o,o.U=e):(e.U=i,i=e,n=e.R)):(r=n.C,n=e),n&&(n.U=i),!r){if(n&&n.C)return void(n.C=!1);do{if(n===this._)break;if(n===i.L){if(t=i.R,t.C&&(t.C=!1,i.C=!0,ir(this,i),t=i.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,ur(this,t),t=i.R),t.C=i.C,i.C=t.R.C=!1,ir(this,i),n=this._;break}}else if(t=i.L,t.C&&(t.C=!1,i.C=!0,ur(this,i),t=i.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,ir(this,t),t=i.L),t.C=i.C,i.C=t.L.C=!1,ur(this,i),n=this._;break}t.C=!0,n=i,i=i.U}while(!n.C);n&&(n.C=!1)}}},ao.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],i=a[0][1],u=a[1][0],o=a[1][1];return ar(e(n),a).cells.forEach(function(e,a){var l=e.edges,c=e.site,f=t[a]=l.length?l.map(function(n){var t=n.start();return[t.x,t.y]}):c.x>=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l<c;)i=f,u=s,f=a[l].edge,s=f.l===o?f.r:f.l,r<u.i&&r<s.i&&cr(o,u,s)<0&&t.push([n[r],n[u.i],n[s.i]])}),t},t.x=function(n){return arguments.length?(u=En(r=n),t):r},t.y=function(n){return arguments.length?(o=En(i=n),t):i},t.clipExtent=function(n){return arguments.length?(a=null==n?sl:n,t):a===sl?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===sl?null:a&&a[1]},t)};var sl=[[-1e6,-1e6],[1e6,1e6]];ao.geom.delaunay=function(n){return ao.geom.voronoi().triangles(n)},ao.geom.quadtree=function(n,t,e,r,i){function u(n){function u(n,t,e,r,i,u,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var l=n.x,f=n.y;if(null!=l)if(xo(l-e)+xo(f-r)<.01)c(n,t,e,r,i,u,o,a);else{var s=n.point;n.x=n.y=n.point=null,c(n,s,l,f,i,u,o,a),c(n,t,e,r,i,u,o,a)}else n.x=e,n.y=r,n.point=t}else c(n,t,e,r,i,u,o,a)}function c(n,t,e,r,i,o,a,l){var c=.5*(i+a),f=.5*(o+l),s=e>=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.x<v&&(v=f.x),f.y<d&&(d=f.y),f.x>y&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p<g;)u(k,n[p],s[p],h[p],v,d,y,m);--p}else n.forEach(k.add);return s=h=n=f=null,k}var o,a=Ce,l=ze;return(o=arguments.length)?(a=fr,l=sr,3===o&&(i=e,r=t,e=t=0),u(n)):(u.x=function(n){return arguments.length?(a=n,u):a},u.y=function(n){return arguments.length?(l=n,u):l},u.extent=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],i=+n[1][1]),u):null==t?null:[[t,e],[r,i]]},u.size=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=e=0,r=+n[0],i=+n[1]),u):null==t?null:[r-t,i-e]},u)},ao.interpolateRgb=vr,ao.interpolateObject=dr,ao.interpolateNumber=yr,ao.interpolateString=mr;var hl=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,pl=new RegExp(hl.source,"g");ao.interpolate=Mr,ao.interpolators=[function(n,t){var e=typeof t;return("string"===e?ua.has(t.toLowerCase())||/^(#|rgb\(|hsl\()/i.test(t)?vr:mr:t instanceof an?vr:Array.isArray(t)?xr:"object"===e&&isNaN(t)?dr:yr)(n,t)}],ao.interpolateArray=xr;var gl=function(){return m},vl=ao.map({linear:gl,poly:Er,quad:function(){return Sr},cubic:function(){return kr},sin:function(){return Ar},exp:function(){return Cr},circle:function(){return zr},elastic:Lr,back:qr,bounce:function(){return Tr}}),dl=ao.map({"in":m,out:_r,"in-out":wr,"out-in":function(n){return wr(_r(n))}});ao.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Jr(n[e]));return t}},ao.layout.chord=function(){function n(){var n,c,s,h,p,g={},v=[],d=ao.range(u),y=[];for(e=[],r=[],n=0,h=-1;++h<u;){for(c=0,p=-1;++p<u;)c+=i[h][p];v.push(c),y.push(ao.range(u)),n+=c}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&y.forEach(function(n,t){n.sort(function(n,e){return a(i[t][n],i[t][e])})}),n=(Ho-f*u)/n,c=0,h=-1;++h<u;){for(s=c,p=-1;++p<u;){var m=d[h],M=y[m][p],x=i[m][M],b=c,_=c+=x*n;g[m+"-"+M]={index:m,subindex:M,startAngle:b,endAngle:_,value:x}}r[m]={index:m,startAngle:s,endAngle:c,value:v[m]},c+=f}for(h=-1;++h<u;)for(p=h-1;++p<u;){var w=g[h+"-"+p],S=g[p+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}l&&t()}function t(){e.sort(function(n,t){return l((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,i,u,o,a,l,c={},f=0;return c.matrix=function(n){return arguments.length?(u=(i=n)&&i.length,e=r=null,c):i},c.padding=function(n){return arguments.length?(f=n,e=r=null,c):f},c.sortGroups=function(n){return arguments.length?(o=n,e=r=null,c):o},c.sortSubgroups=function(n){return arguments.length?(a=n,e=null,c):a},c.sortChords=function(n){return arguments.length?(l=n,e&&t(),c):l},c.chords=function(){return e||n(),e},c.groups=function(){return r||n(),r},c},ao.layout.force=function(){function n(n){return function(t,e,r,i){if(t.point!==n){var u=t.cx-n.x,o=t.cy-n.y,a=i-e,l=u*u+o*o;if(l>a*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++l<f;)if(!isNaN(o=a[l][n]))return o;return Math.random()*r}var t,e,r,i=M.length,c=x.length,s=f[0],v=f[1];for(t=0;i>t;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++c<o;)n(a=u[c],e,l=a.value*r,i),e+=l}}function t(n){var e=n.children,r=0;if(e&&(i=e.length))for(var i,u=-1;++u<i;)r=Math.max(r,t(e[u]));return 1+r}function e(e,u){var o=r.call(this,e,u);return n(o[0],0,i[0],i[1]/t(o[0])),o}var r=ao.layout.hierarchy(),i=[1,1];return e.size=function(n){return arguments.length?(i=n,e):i},ii(e,r)},ao.layout.pie=function(){function n(o){var a,l=o.length,c=o.map(function(e,r){return+t.call(n,e,r)}),f=+("function"==typeof r?r.apply(this,arguments):r),s=("function"==typeof i?i.apply(this,arguments):i)-f,h=Math.min(Math.abs(s)/l,+("function"==typeof u?u.apply(this,arguments):u)),p=h*(0>s?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u<p;)o=l[u]=[],o.dx=s[u+1]-(o.x=s[u]),o.y=0;if(p>0)for(u=-1;++u<h;)a=c[u],a>=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.x<p.x&&(p=n),n.x>g.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++i<u;)r=(e=n[i]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(u>e&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0;
if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++u<o;)i=n[u],i.x=a,i.y=c,i.dy=f,a+=i.dx=Math.min(e.x+e.dx-a,f?l(i.area/f):0);i.z=!0,i.dx+=e.x+e.dx-a,e.y+=f,e.dy-=f}else{for((r||f>e.dx)&&(f=e.dx);++u<o;)i=n[u],i.x=a,i.y=c,i.dx=f,c+=i.dy=Math.min(e.y+e.dy-c,f?l(i.area/f):0);i.z=!1,i.dy+=e.y+e.dy-c,e.x+=f,e.dx-=f}}function u(r){var i=o||a(r),u=i[0];return u.x=u.y=0,u.value?(u.dx=c[0],u.dy=c[1]):u.dx=u.dy=0,o&&a.revalue(u),n([u],u.dx*u.dy/u.value),(o?e:t)(u),h&&(o=i),i}var o,a=ao.layout.hierarchy(),l=Math.round,c=[1,1],f=null,s=Oi,h=!1,p="squarify",g=.5*(1+Math.sqrt(5));return u.size=function(n){return arguments.length?(c=n,u):c},u.padding=function(n){function t(t){var e=n.call(u,t,t.depth);return null==e?Oi(t):Ii(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return Ii(t,n)}if(!arguments.length)return f;var r;return s=null==(f=n)?Oi:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,u},u.round=function(n){return arguments.length?(l=n?Math.round:Number,u):l!=Number},u.sticky=function(n){return arguments.length?(h=n,o=null,u):h},u.ratio=function(n){return arguments.length?(g=n,u):g},u.mode=function(n){return arguments.length?(p=n+"",u):p},ii(u,a)},ao.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++a<l;){u.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(e=c[f])&&Qu(e,f,i,r,o),t.push(e)}return Wu(u,i,r)},Co.interrupt=function(n){return this.each(null==n?Il:Bu(Ku(n)))};var Hl,Ol,Il=Bu(Ku()),Yl=[],Zl=0;Yl.call=Co.call,Yl.empty=Co.empty,Yl.node=Co.node,Yl.size=Co.size,ao.transition=function(n,t){return n&&n.transition?Hl?n.transition(t):n:ao.selection().transition(n)},ao.transition.prototype=Yl,Yl.select=function(n){var t,e,r,i=this.id,u=this.namespace,o=[];n=A(n);for(var a=-1,l=this.length;++a<l;){o.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(r=c[f])&&(e=n.call(r,r.__data__,f,a))?("__data__"in r&&(e.__data__=r.__data__),Qu(e,f,u,i,r[u][i]),t.push(e)):t.push(null)}return Wu(o,u,i)},Yl.selectAll=function(n){var t,e,r,i,u,o=this.id,a=this.namespace,l=[];n=C(n);for(var c=-1,f=this.length;++c<f;)for(var s=this[c],h=-1,p=s.length;++h<p;)if(r=s[h]){u=r[a][o],e=n.call(r,r.__data__,h,c),l.push(t=[]);for(var g=-1,v=e.length;++g<v;)(i=e[g])&&Qu(i,g,a,o,u),t.push(i)}return Wu(l,a,o)},Yl.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]<M[0])],L[1]=h[+(n[1]<M[1])]):M=null),E&&y(n,c,0)&&(r(k),t=!0),A&&y(n,f,1)&&(i(k),t=!0),t&&(e(k),w({type:"brush",mode:C?"move":"resize"}))}function y(n,t,e){var r,i,u=Zi(t),l=u[0],c=u[1],f=L[e],v=e?h:s,d=v[1]-v[0];return C&&(l-=f,c-=d+f),r=(e?g:p)?Math.max(l,Math.min(c,n[e])):n[e],C?i=(r+=f)+d:(M&&(f=Math.max(l,Math.min(c,2*M[e]-r))),r>f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}(); $(function() {
var $window = $(window)
, $top_link = $('#toplink')
, $body = $('body, html')
, offset = $('#code').offset().top
, hidePopover = function ($target) {
$target.data('popover-hover', false);
setTimeout(function () {
if (!$target.data('popover-hover')) {
$target.popover('hide');
}
}, 300);
};
$top_link.hide().click(function(event) {
event.preventDefault();
$body.animate({scrollTop:0}, 800);
});
$window.scroll(function() {
if($window.scrollTop() > offset) {
$top_link.fadeIn();
} else {
$top_link.fadeOut();
}
}).scroll();
$('.popin')
.popover({trigger: 'manual'})
.on({
'mouseenter.popover': function () {
var $target = $(this);
var $container = $target.children().first();
$target.data('popover-hover', true);
// popover already displayed
if ($target.next('.popover').length) {
return;
}
// show the popover
$container.popover('show');
// register mouse events on the popover
$target.next('.popover:not(.popover-initialized)')
.on({
'mouseenter': function () {
$target.data('popover-hover', true);
},
'mouseleave': function () {
hidePopover($container);
}
})
.addClass('popover-initialized');
},
'mouseleave.popover': function () {
hidePopover($(this).children().first());
}
});
});
/*
Copyright (C) Federico Zivolo 2019
Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
*/(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?pe:10===e?se:pe||se}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:'top',o='top'===t?'scrollTop':'scrollLeft',n=e.nodeName;if('BODY'===n||'HTML'===n){var i=e.ownerDocument.documentElement,r=e.ownerDocument.scrollingElement||i;return r[o]}return e[o]}function f(e,t){var o=2<arguments.length&&void 0!==arguments[2]&&arguments[2],n=l(t,'top'),i=l(t,'left'),r=o?-1:1;return e.top+=n*r,e.bottom+=n*r,e.left+=i*r,e.right+=i*r,e}function m(e,t){var o='x'===t?'Left':'Top',n='Left'==o?'Right':'Bottom';return parseFloat(e['border'+o+'Width'],10)+parseFloat(e['border'+n+'Width'],10)}function h(e,t,o,n){return ee(t['offset'+e],t['scroll'+e],o['client'+e],o['offset'+e],o['scroll'+e],r(10)?parseInt(o['offset'+e])+parseInt(n['margin'+('Height'===e?'Top':'Left')])+parseInt(n['margin'+('Height'===e?'Bottom':'Right')]):0)}function c(e){var t=e.body,o=e.documentElement,n=r(10)&&getComputedStyle(o);return{height:h('Height',t,o,n),width:h('Width',t,o,n)}}function g(e){return fe({},e,{right:e.left+e.width,bottom:e.top+e.height})}function u(e){var o={};try{if(r(10)){o=e.getBoundingClientRect();var n=l(e,'top'),i=l(e,'left');o.top+=n,o.left+=i,o.bottom+=n,o.right+=i}else o=e.getBoundingClientRect()}catch(t){}var p={left:o.left,top:o.top,width:o.right-o.left,height:o.bottom-o.top},s='HTML'===e.nodeName?c(e.ownerDocument):{},d=s.width||e.clientWidth||p.right-p.left,a=s.height||e.clientHeight||p.bottom-p.top,f=e.offsetWidth-d,h=e.offsetHeight-a;if(f||h){var u=t(e);f-=m(u,'x'),h-=m(u,'y'),p.width-=f,p.height-=h}return g(p)}function b(e,o){var i=2<arguments.length&&void 0!==arguments[2]&&arguments[2],p=r(10),s='HTML'===o.nodeName,d=u(e),a=u(o),l=n(e),m=t(o),h=parseFloat(m.borderTopWidth,10),c=parseFloat(m.borderLeftWidth,10);i&&s&&(a.top=ee(a.top,0),a.left=ee(a.left,0));var b=g({top:d.top-a.top-h,left:d.left-a.left-c,width:d.width,height:d.height});if(b.marginTop=0,b.marginLeft=0,!p&&s){var w=parseFloat(m.marginTop,10),y=parseFloat(m.marginLeft,10);b.top-=h-w,b.bottom-=h-w,b.left-=c-y,b.right-=c-y,b.marginTop=w,b.marginLeft=y}return(p&&!i?o.contains(l):o===l&&'BODY'!==l.nodeName)&&(b=f(b,o)),b}function w(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=e.ownerDocument.documentElement,n=b(e,o),i=ee(o.clientWidth,window.innerWidth||0),r=ee(o.clientHeight,window.innerHeight||0),p=t?0:l(o),s=t?0:l(o,'left'),d={top:p-n.top+n.marginTop,left:s-n.left+n.marginLeft,width:i,height:r};return g(d)}function y(e){var n=e.nodeName;if('BODY'===n||'HTML'===n)return!1;if('fixed'===t(e,'position'))return!0;var i=o(e);return!!i&&y(i)}function E(e){if(!e||!e.parentElement||r())return document.documentElement;for(var o=e.parentElement;o&&'none'===t(o,'transform');)o=o.parentElement;return o||document.documentElement}function v(e,t,i,r){var p=4<arguments.length&&void 0!==arguments[4]&&arguments[4],s={top:0,left:0},d=p?E(e):a(e,t);if('viewport'===r)s=w(d,p);else{var l;'scrollParent'===r?(l=n(o(t)),'BODY'===l.nodeName&&(l=e.ownerDocument.documentElement)):'window'===r?l=e.ownerDocument.documentElement:l=r;var f=b(l,d,p);if('HTML'===l.nodeName&&!y(d)){var m=c(e.ownerDocument),h=m.height,g=m.width;s.top+=f.top-f.marginTop,s.bottom=h+f.top,s.left+=f.left-f.marginLeft,s.right=g+f.left}else s=f}i=i||0;var u='number'==typeof i;return s.left+=u?i:i.left||0,s.top+=u?i:i.top||0,s.right-=u?i:i.right||0,s.bottom-=u?i:i.bottom||0,s}function x(e){var t=e.width,o=e.height;return t*o}function O(e,t,o,n,i){var r=5<arguments.length&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf('auto'))return e;var p=v(o,n,r,i),s={top:{width:p.width,height:t.top-p.top},right:{width:p.right-t.right,height:p.height},bottom:{width:p.width,height:p.bottom-t.bottom},left:{width:t.left-p.left,height:p.height}},d=Object.keys(s).map(function(e){return fe({key:e},s[e],{area:x(s[e])})}).sort(function(e,t){return t.area-e.area}),a=d.filter(function(e){var t=e.width,n=e.height;return t>=o.clientWidth&&n>=o.clientHeight}),l=0<a.length?a[0].key:d[0].key,f=e.split('-')[1];return l+(f?'-'+f:'')}function L(e,t,o){var n=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,i=n?E(t):a(t,o);return b(o,i,n)}function S(e){var t=e.ownerDocument.defaultView,o=t.getComputedStyle(e),n=parseFloat(o.marginTop||0)+parseFloat(o.marginBottom||0),i=parseFloat(o.marginLeft||0)+parseFloat(o.marginRight||0),r={width:e.offsetWidth+i,height:e.offsetHeight+n};return r}function T(e){var t={left:'right',right:'left',bottom:'top',top:'bottom'};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function C(e,t,o){o=o.split('-')[0];var n=S(e),i={width:n.width,height:n.height},r=-1!==['right','left'].indexOf(o),p=r?'top':'left',s=r?'left':'top',d=r?'height':'width',a=r?'width':'height';return i[p]=t[p]+t[d]/2-n[d]/2,i[s]=o===s?t[s]-n[a]:t[T(s)],i}function D(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function N(e,t,o){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===o});var n=D(e,function(e){return e[t]===o});return e.indexOf(n)}function P(t,o,n){var i=void 0===n?t:t.slice(0,N(t,'name',n));return i.forEach(function(t){t['function']&&console.warn('`modifier.function` is deprecated, use `modifier.fn`!');var n=t['function']||t.fn;t.enabled&&e(n)&&(o.offsets.popper=g(o.offsets.popper),o.offsets.reference=g(o.offsets.reference),o=n(o,t))}),o}function k(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=L(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=O(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=C(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?'fixed':'absolute',e=P(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function W(e,t){return e.some(function(e){var o=e.name,n=e.enabled;return n&&o===t})}function B(e){for(var t=[!1,'ms','Webkit','Moz','O'],o=e.charAt(0).toUpperCase()+e.slice(1),n=0;n<t.length;n++){var i=t[n],r=i?''+i+o:e;if('undefined'!=typeof document.body.style[r])return r}return null}function H(){return this.state.isDestroyed=!0,W(this.modifiers,'applyStyle')&&(this.popper.removeAttribute('x-placement'),this.popper.style.position='',this.popper.style.top='',this.popper.style.left='',this.popper.style.right='',this.popper.style.bottom='',this.popper.style.willChange='',this.popper.style[B('transform')]=''),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function A(e){var t=e.ownerDocument;return t?t.defaultView:window}function M(e,t,o,i){var r='BODY'===e.nodeName,p=r?e.ownerDocument.defaultView:e;p.addEventListener(t,o,{passive:!0}),r||M(n(p.parentNode),t,o,i),i.push(p)}function F(e,t,o,i){o.updateBound=i,A(e).addEventListener('resize',o.updateBound,{passive:!0});var r=n(e);return M(r,'scroll',o.updateBound,o.scrollParents),o.scrollElement=r,o.eventsEnabled=!0,o}function I(){this.state.eventsEnabled||(this.state=F(this.reference,this.options,this.state,this.scheduleUpdate))}function R(e,t){return A(e).removeEventListener('resize',t.updateBound),t.scrollParents.forEach(function(e){e.removeEventListener('scroll',t.updateBound)}),t.updateBound=null,t.scrollParents=[],t.scrollElement=null,t.eventsEnabled=!1,t}function U(){this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=R(this.reference,this.state))}function Y(e){return''!==e&&!isNaN(parseFloat(e))&&isFinite(e)}function V(e,t){Object.keys(t).forEach(function(o){var n='';-1!==['width','height','top','right','bottom','left'].indexOf(o)&&Y(t[o])&&(n='px'),e.style[o]=t[o]+n})}function j(e,t){Object.keys(t).forEach(function(o){var n=t[o];!1===n?e.removeAttribute(o):e.setAttribute(o,t[o])})}function q(e,t){var o=e.offsets,n=o.popper,i=o.reference,r=$,p=function(e){return e},s=r(i.width),d=r(n.width),a=-1!==['left','right'].indexOf(e.placement),l=-1!==e.placement.indexOf('-'),f=t?a||l||s%2==d%2?r:Z:p,m=t?r:p;return{left:f(1==s%2&&1==d%2&&!l&&t?n.left-1:n.left),top:m(n.top),bottom:m(n.bottom),right:f(n.right)}}function K(e,t,o){var n=D(e,function(e){var o=e.name;return o===t}),i=!!n&&e.some(function(e){return e.name===o&&e.enabled&&e.order<n.order});if(!i){var r='`'+t+'`';console.warn('`'+o+'`'+' modifier is required by '+r+' modifier in order to work, be sure to include it before '+r+'!')}return i}function z(e){return'end'===e?'start':'start'===e?'end':e}function G(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=ce.indexOf(e),n=ce.slice(o+1).concat(ce.slice(0,o));return t?n.reverse():n}function _(e,t,o,n){var i=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+i[1],p=i[2];if(!r)return e;if(0===p.indexOf('%')){var s;switch(p){case'%p':s=o;break;case'%':case'%r':default:s=n;}var d=g(s);return d[t]/100*r}if('vh'===p||'vw'===p){var a;return a='vh'===p?ee(document.documentElement.clientHeight,window.innerHeight||0):ee(document.documentElement.clientWidth,window.innerWidth||0),a/100*r}return r}function X(e,t,o,n){var i=[0,0],r=-1!==['right','left'].indexOf(n),p=e.split(/(\+|\-)/).map(function(e){return e.trim()}),s=p.indexOf(D(p,function(e){return-1!==e.search(/,|\s/)}));p[s]&&-1===p[s].indexOf(',')&&console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');var d=/\s*,\s*|\s+/,a=-1===s?[p]:[p.slice(0,s).concat([p[s].split(d)[0]]),[p[s].split(d)[1]].concat(p.slice(s+1))];return a=a.map(function(e,n){var i=(1===n?!r:r)?'height':'width',p=!1;return e.reduce(function(e,t){return''===e[e.length-1]&&-1!==['+','-'].indexOf(t)?(e[e.length-1]=t,p=!0,e):p?(e[e.length-1]+=t,p=!1,e):e.concat(t)},[]).map(function(e){return _(e,i,t,o)})}),a.forEach(function(e,t){e.forEach(function(o,n){Y(o)&&(i[t]+=o*('-'===e[n-1]?-1:1))})}),i}function J(e,t){var o,n=t.offset,i=e.placement,r=e.offsets,p=r.popper,s=r.reference,d=i.split('-')[0];return o=Y(+n)?[+n,0]:X(n,p,s,d),'left'===d?(p.top+=o[0],p.left-=o[1]):'right'===d?(p.top+=o[0],p.left+=o[1]):'top'===d?(p.left+=o[0],p.top-=o[1]):'bottom'===d&&(p.left+=o[0],p.top+=o[1]),e.popper=p,e}for(var Q=Math.min,Z=Math.floor,$=Math.round,ee=Math.max,te='undefined'!=typeof window&&'undefined'!=typeof document,oe=['Edge','Trident','Firefox'],ne=0,ie=0;ie<oe.length;ie+=1)if(te&&0<=navigator.userAgent.indexOf(oe[ie])){ne=1;break}var i=te&&window.Promise,re=i?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},ne))}},pe=te&&!!(window.MSInputMethodContext&&document.documentMode),se=te&&/MSIE 10/.test(navigator.userAgent),de=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},ae=function(){function e(e,t){for(var o,n=0;n<t.length;n++)o=t[n],o.enumerable=o.enumerable||!1,o.configurable=!0,'value'in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}return function(t,o,n){return o&&e(t.prototype,o),n&&e(t,n),t}}(),le=function(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e},fe=Object.assign||function(e){for(var t,o=1;o<arguments.length;o++)for(var n in t=arguments[o],t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e},me=te&&/Firefox/i.test(navigator.userAgent),he=['auto-start','auto','auto-end','top-start','top','top-end','right-start','right','right-end','bottom-end','bottom','bottom-start','left-end','left','left-start'],ce=he.slice(3),ge={FLIP:'flip',CLOCKWISE:'clockwise',COUNTERCLOCKWISE:'counterclockwise'},ue=function(){function t(o,n){var i=this,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};de(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=re(this.update.bind(this)),this.options=fe({},t.Defaults,r),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=o&&o.jquery?o[0]:o,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(fe({},t.Defaults.modifiers,r.modifiers)).forEach(function(e){i.options.modifiers[e]=fe({},t.Defaults.modifiers[e]||{},r.modifiers?r.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return fe({name:e},i.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(t){t.enabled&&e(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)}),this.update();var p=this.options.eventsEnabled;p&&this.enableEventListeners(),this.state.eventsEnabled=p}return ae(t,[{key:'update',value:function(){return k.call(this)}},{key:'destroy',value:function(){return H.call(this)}},{key:'enableEventListeners',value:function(){return I.call(this)}},{key:'disableEventListeners',value:function(){return U.call(this)}}]),t}();return ue.Utils=('undefined'==typeof window?global:window).PopperUtils,ue.placements=he,ue.Defaults={placement:'bottom',positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,o=t.split('-')[0],n=t.split('-')[1];if(n){var i=e.offsets,r=i.reference,p=i.popper,s=-1!==['bottom','top'].indexOf(o),d=s?'left':'top',a=s?'width':'height',l={start:le({},d,r[d]),end:le({},d,r[d]+r[a]-p[a])};e.offsets.popper=fe({},p,l[n])}return e}},offset:{order:200,enabled:!0,fn:J,offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,t){var o=t.boundariesElement||p(e.instance.popper);e.instance.reference===o&&(o=p(o));var n=B('transform'),i=e.instance.popper.style,r=i.top,s=i.left,d=i[n];i.top='',i.left='',i[n]='';var a=v(e.instance.popper,e.instance.reference,t.padding,o,e.positionFixed);i.top=r,i.left=s,i[n]=d,t.boundaries=a;var l=t.priority,f=e.offsets.popper,m={primary:function(e){var o=f[e];return f[e]<a[e]&&!t.escapeWithReference&&(o=ee(f[e],a[e])),le({},e,o)},secondary:function(e){var o='right'===e?'left':'top',n=f[o];return f[e]>a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),le({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=fe({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]<r(n[d])&&(e.offsets.popper[d]=r(n[d])-o[a]),o[d]>r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-u<s[m]&&(e.offsets.popper[m]-=s[m]-(d[c]-u)),d[m]+u>s[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},le(n,m,$(v)),le(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ge.FLIP:p=[n,i];break;case ge.CLOCKWISE:p=G(n);break;case ge.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)<f(l.right)||'top'===n&&f(a.bottom)>f(l.top)||'bottom'===n&&f(a.top)<f(l.bottom),h=f(a.left)<f(o.left),c=f(a.right)>f(o.right),g=f(a.top)<f(o.top),u=f(a.bottom)>f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=fe({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottom<o.top||t.left>o.right||t.top>o.bottom||t.right<o.left){if(!0===e.hide)return e;e.hide=!0,e.attributes['x-out-of-boundaries']=''}else{if(!1===e.hide)return e;e.hide=!1,e.attributes['x-out-of-boundaries']=!1}return e}},computeStyle:{order:850,enabled:!0,fn:function(e,t){var o=t.x,n=t.y,i=e.offsets.popper,r=D(e.instance.modifiers,function(e){return'applyStyle'===e.name}).gpuAcceleration;void 0!==r&&console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');var s,d,a=void 0===r?t.gpuAcceleration:r,l=p(e.instance.popper),f=u(l),m={position:i.position},h=q(e,2>window.devicePixelRatio||!me),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=fe({},E,e.attributes),e.styles=fe({},m,e.styles),e.arrowStyles=fe({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ue});
//# sourceMappingURL=popper.min.js.map
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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\File as FileNode;
use SebastianBergmann\CodeCoverage\Util;
/**
* Renders a file node.
*/
final class File extends Renderer
{
/**
* @var int
*/
private $htmlSpecialCharsFlags = \ENT_COMPAT | \ENT_HTML401 | \ENT_SUBSTITUTE;
/**
* @throws \RuntimeException
*/
public function render(FileNode $node, string $file): void
{
$template = new \Text_Template($this->templatePath . 'file.html', '{{', '}}');
$template->setVar(
[
'items' => $this->renderItems($node),
'lines' => $this->renderSource($node),
]
);
$this->setCommonTemplateVariables($template, $node);
$template->renderTo($file);
}
protected function renderItems(FileNode $node): string
{
$template = new \Text_Template($this->templatePath . 'file_item.html', '{{', '}}');
$methodItemTemplate = new \Text_Template(
$this->templatePath . 'method_item.html',
'{{',
'}}'
);
$items = $this->renderItemTemplate(
$template,
[
'name' => 'Total',
'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(),
'crap' => '<abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr>',
]
);
$items .= $this->renderFunctionItems(
$node->getFunctions(),
$methodItemTemplate
);
$items .= $this->renderTraitOrClassItems(
$node->getTraits(),
$template,
$methodItemTemplate
);
$items .= $this->renderTraitOrClassItems(
$node->getClasses(),
$template,
$methodItemTemplate
);
return $items;
}
protected function renderTraitOrClassItems(array $items, \Text_Template $template, \Text_Template $methodItemTemplate): string
{
$buffer = '';
if (empty($items)) {
return $buffer;
}
foreach ($items as $name => $item) {
$numMethods = 0;
$numTestedMethods = 0;
foreach ($item['methods'] as $method) {
if ($method['executableLines'] > 0) {
$numMethods++;
if ($method['executedLines'] === $method['executableLines']) {
$numTestedMethods++;
}
}
}
if ($item['executableLines'] > 0) {
$numClasses = 1;
$numTestedClasses = $numTestedMethods == $numMethods ? 1 : 0;
$linesExecutedPercentAsString = Util::percent(
$item['executedLines'],
$item['executableLines'],
true
);
} else {
$numClasses = 'n/a';
$numTestedClasses = 'n/a';
$linesExecutedPercentAsString = 'n/a';
}
$buffer .= $this->renderItemTemplate(
$template,
[
'name' => $this->abbreviateClassName($name),
'numClasses' => $numClasses,
'numTestedClasses' => $numTestedClasses,
'numMethods' => $numMethods,
'numTestedMethods' => $numTestedMethods,
'linesExecutedPercent' => Util::percent(
$item['executedLines'],
$item['executableLines'],
false
),
'linesExecutedPercentAsString' => $linesExecutedPercentAsString,
'numExecutedLines' => $item['executedLines'],
'numExecutableLines' => $item['executableLines'],
'testedMethodsPercent' => Util::percent(
$numTestedMethods,
$numMethods
),
'testedMethodsPercentAsString' => Util::percent(
$numTestedMethods,
$numMethods,
true
),
'testedClassesPercent' => Util::percent(
$numTestedMethods == $numMethods ? 1 : 0,
1
),
'testedClassesPercentAsString' => Util::percent(
$numTestedMethods == $numMethods ? 1 : 0,
1,
true
),
'crap' => $item['crap'],
]
);
foreach ($item['methods'] as $method) {
$buffer .= $this->renderFunctionOrMethodItem(
$methodItemTemplate,
$method,
'&nbsp;'
);
}
}
return $buffer;
}
protected function renderFunctionItems(array $functions, \Text_Template $template): string
{
if (empty($functions)) {
return '';
}
$buffer = '';
foreach ($functions as $function) {
$buffer .= $this->renderFunctionOrMethodItem(
$template,
$function
);
}
return $buffer;
}
protected function renderFunctionOrMethodItem(\Text_Template $template, array $item, string $indent = ''): string
{
$numMethods = 0;
$numTestedMethods = 0;
if ($item['executableLines'] > 0) {
$numMethods = 1;
if ($item['executedLines'] === $item['executableLines']) {
$numTestedMethods = 1;
}
}
return $this->renderItemTemplate(
$template,
[
'name' => \sprintf(
'%s<a href="#%d"><abbr title="%s">%s</abbr></a>',
$indent,
$item['startLine'],
\htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags),
$item['functionName'] ?? $item['methodName']
),
'numMethods' => $numMethods,
'numTestedMethods' => $numTestedMethods,
'linesExecutedPercent' => Util::percent(
$item['executedLines'],
$item['executableLines']
),
'linesExecutedPercentAsString' => Util::percent(
$item['executedLines'],
$item['executableLines'],
true
),
'numExecutedLines' => $item['executedLines'],
'numExecutableLines' => $item['executableLines'],
'testedMethodsPercent' => Util::percent(
$numTestedMethods,
1
),
'testedMethodsPercentAsString' => Util::percent(
$numTestedMethods,
1,
true
),
'crap' => $item['crap'],
]
);
}
protected function renderSource(FileNode $node): string
{
$coverageData = $node->getCoverageData();
$testData = $node->getTestData();
$codeLines = $this->loadFile($node->getPath());
$lines = '';
$i = 1;
foreach ($codeLines as $line) {
$trClass = '';
$popoverContent = '';
$popoverTitle = '';
if (\array_key_exists($i, $coverageData)) {
$numTests = ($coverageData[$i] ? \count($coverageData[$i]) : 0);
if ($coverageData[$i] === null) {
$trClass = ' class="warning"';
} elseif ($numTests == 0) {
$trClass = ' class="danger"';
} else {
$lineCss = 'covered-by-large-tests';
$popoverContent = '<ul>';
if ($numTests > 1) {
$popoverTitle = $numTests . ' tests cover line ' . $i;
} else {
$popoverTitle = '1 test covers line ' . $i;
}
foreach ($coverageData[$i] as $test) {
if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') {
$lineCss = 'covered-by-medium-tests';
} elseif ($testData[$test]['size'] == 'small') {
$lineCss = 'covered-by-small-tests';
}
switch ($testData[$test]['status']) {
case 0:
switch ($testData[$test]['size']) {
case 'small':
$testCSS = ' class="covered-by-small-tests"';
break;
case 'medium':
$testCSS = ' class="covered-by-medium-tests"';
break;
default:
$testCSS = ' class="covered-by-large-tests"';
break;
}
break;
case 1:
case 2:
$testCSS = ' class="warning"';
break;
case 3:
$testCSS = ' class="danger"';
break;
case 4:
$testCSS = ' class="danger"';
break;
default:
$testCSS = '';
}
$popoverContent .= \sprintf(
'<li%s>%s</li>',
$testCSS,
\htmlspecialchars($test, $this->htmlSpecialCharsFlags)
);
}
$popoverContent .= '</ul>';
$trClass = ' class="' . $lineCss . ' popin"';
}
}
$popover = '';
if (!empty($popoverTitle)) {
$popover = \sprintf(
' data-title="%s" data-content="%s" data-placement="top" data-html="true"',
$popoverTitle,
\htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags)
);
}
$lines .= \sprintf(
' <tr%s><td%s><div align="right"><a name="%d"></a><a href="#%d">%d</a></div></td><td class="codeLine">%s</td></tr>' . "\n",
$trClass,
$popover,
$i,
$i,
$i,
$line
);
$i++;
}
return $lines;
}
/**
* @param string $file
*/
protected function loadFile($file): array
{
$buffer = \file_get_contents($file);
$tokens = \token_get_all($buffer);
$result = [''];
$i = 0;
$stringFlag = false;
$fileEndsWithNewLine = \substr($buffer, -1) == "\n";
unset($buffer);
foreach ($tokens as $j => $token) {
if (\is_string($token)) {
if ($token === '"' && $tokens[$j - 1] !== '\\') {
$result[$i] .= \sprintf(
'<span class="string">%s</span>',
\htmlspecialchars($token, $this->htmlSpecialCharsFlags)
);
$stringFlag = !$stringFlag;
} else {
$result[$i] .= \sprintf(
'<span class="keyword">%s</span>',
\htmlspecialchars($token, $this->htmlSpecialCharsFlags)
);
}
continue;
}
[$token, $value] = $token;
$value = \str_replace(
["\t", ' '],
['&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'],
\htmlspecialchars($value, $this->htmlSpecialCharsFlags)
);
if ($value === "\n") {
$result[++$i] = '';
} else {
$lines = \explode("\n", $value);
foreach ($lines as $jj => $line) {
$line = \trim($line);
if ($line !== '') {
if ($stringFlag) {
$colour = 'string';
} else {
switch ($token) {
case \T_INLINE_HTML:
$colour = 'html';
break;
case \T_COMMENT:
case \T_DOC_COMMENT:
$colour = 'comment';
break;
case \T_ABSTRACT:
case \T_ARRAY:
case \T_AS:
case \T_BREAK:
case \T_CALLABLE:
case \T_CASE:
case \T_CATCH:
case \T_CLASS:
case \T_CLONE:
case \T_CONTINUE:
case \T_DEFAULT:
case \T_ECHO:
case \T_ELSE:
case \T_ELSEIF:
case \T_EMPTY:
case \T_ENDDECLARE:
case \T_ENDFOR:
case \T_ENDFOREACH:
case \T_ENDIF:
case \T_ENDSWITCH:
case \T_ENDWHILE:
case \T_EXIT:
case \T_EXTENDS:
case \T_FINAL:
case \T_FINALLY:
case \T_FOREACH:
case \T_FUNCTION:
case \T_GLOBAL:
case \T_IF:
case \T_IMPLEMENTS:
case \T_INCLUDE:
case \T_INCLUDE_ONCE:
case \T_INSTANCEOF:
case \T_INSTEADOF:
case \T_INTERFACE:
case \T_ISSET:
case \T_LOGICAL_AND:
case \T_LOGICAL_OR:
case \T_LOGICAL_XOR:
case \T_NAMESPACE:
case \T_NEW:
case \T_PRIVATE:
case \T_PROTECTED:
case \T_PUBLIC:
case \T_REQUIRE:
case \T_REQUIRE_ONCE:
case \T_RETURN:
case \T_STATIC:
case \T_THROW:
case \T_TRAIT:
case \T_TRY:
case \T_UNSET:
case \T_USE:
case \T_VAR:
case \T_WHILE:
case \T_YIELD:
$colour = 'keyword';
break;
default:
$colour = 'default';
}
}
$result[$i] .= \sprintf(
'<span class="%s">%s</span>',
$colour,
$line
);
}
if (isset($lines[$jj + 1])) {
$result[++$i] = '';
}
}
}
}
if ($fileEndsWithNewLine) {
unset($result[\count($result) - 1]);
}
return $result;
}
private function abbreviateClassName(string $className): string
{
$tmp = \explode('\\', $className);
if (\count($tmp) > 1) {
$className = \sprintf(
'<abbr title="%s">%s</abbr>',
$className,
\array_pop($tmp)
);
}
return $className;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\RuntimeException;
/**
* Generates an HTML report from a code coverage object.
*/
final class Facade
{
/**
* @var string
*/
private $templatePath;
/**
* @var string
*/
private $generator;
/**
* @var int
*/
private $lowUpperBound;
/**
* @var int
*/
private $highLowerBound;
public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, string $generator = '')
{
$this->generator = $generator;
$this->highLowerBound = $highLowerBound;
$this->lowUpperBound = $lowUpperBound;
$this->templatePath = __DIR__ . '/Renderer/Template/';
}
/**
* @throws RuntimeException
* @throws \InvalidArgumentException
* @throws \RuntimeException
*/
public function process(CodeCoverage $coverage, string $target): void
{
$target = $this->getDirectory($target);
$report = $coverage->getReport();
if (!isset($_SERVER['REQUEST_TIME'])) {
$_SERVER['REQUEST_TIME'] = \time();
}
$date = \date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']);
$dashboard = new Dashboard(
$this->templatePath,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound
);
$directory = new Directory(
$this->templatePath,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound
);
$file = new File(
$this->templatePath,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound
);
$directory->render($report, $target . 'index.html');
$dashboard->render($report, $target . 'dashboard.html');
foreach ($report as $node) {
$id = $node->getId();
if ($node instanceof DirectoryNode) {
if (!$this->createDirectory($target . $id)) {
throw new \RuntimeException(\sprintf('Directory "%s" was not created', $target . $id));
}
$directory->render($node, $target . $id . '/index.html');
$dashboard->render($node, $target . $id . '/dashboard.html');
} else {
$dir = \dirname($target . $id);
if (!$this->createDirectory($dir)) {
throw new \RuntimeException(\sprintf('Directory "%s" was not created', $dir));
}
$file->render($node, $target . $id . '.html');
}
}
$this->copyFiles($target);
}
/**
* @throws RuntimeException
*/
private function copyFiles(string $target): void
{
$dir = $this->getDirectory($target . '_css');
\copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css');
\copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css');
\copy($this->templatePath . 'css/style.css', $dir . 'style.css');
\copy($this->templatePath . 'css/custom.css', $dir . 'custom.css');
\copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css');
$dir = $this->getDirectory($target . '_icons');
\copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg');
\copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg');
$dir = $this->getDirectory($target . '_js');
\copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js');
\copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js');
\copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js');
\copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js');
\copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js');
\copy($this->templatePath . 'js/file.js', $dir . 'file.js');
}
/**
* @throws RuntimeException
*/
private function getDirectory(string $directory): string
{
if (\substr($directory, -1, 1) != \DIRECTORY_SEPARATOR) {
$directory .= \DIRECTORY_SEPARATOR;
}
if (!$this->createDirectory($directory)) {
throw new RuntimeException(
\sprintf(
'Directory "%s" does not exist.',
$directory
)
);
}
return $directory;
}
private function createDirectory(string $directory): bool
{
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Report;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\RuntimeException;
/**
* Uses var_export() to write a SebastianBergmann\CodeCoverage\CodeCoverage object to a file.
*/
final class PHP
{
/**
* @throws \SebastianBergmann\CodeCoverage\RuntimeException
*/
public function process(CodeCoverage $coverage, ?string $target = null): string
{
$filter = $coverage->filter();
$buffer = \sprintf(
'<?php
$coverage = new SebastianBergmann\CodeCoverage\CodeCoverage;
$coverage->setData(%s);
$coverage->setTests(%s);
$filter = $coverage->filter();
$filter->setWhitelistedFiles(%s);
return $coverage;',
\var_export($coverage->getData(true), true),
\var_export($coverage->getTests(), true),
\var_export($filter->getWhitelistedFiles(), true)
);
if ($target !== null) {
if (!$this->createDirectory(\dirname($target))) {
throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target)));
}
if (@\file_put_contents($target, $buffer) === false) {
throw new RuntimeException(
\sprintf(
'Could not write to "%s',
$target
)
);
}
}
return $buffer;
}
private function createDirectory(string $directory): bool
{
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Report;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\RuntimeException;
/**
* Generates a Clover XML logfile from a code coverage object.
*/
final class Clover
{
/**
* @throws \RuntimeException
*/
public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string
{
$xmlDocument = new \DOMDocument('1.0', 'UTF-8');
$xmlDocument->formatOutput = true;
$xmlCoverage = $xmlDocument->createElement('coverage');
$xmlCoverage->setAttribute('generated', (string) $_SERVER['REQUEST_TIME']);
$xmlDocument->appendChild($xmlCoverage);
$xmlProject = $xmlDocument->createElement('project');
$xmlProject->setAttribute('timestamp', (string) $_SERVER['REQUEST_TIME']);
if (\is_string($name)) {
$xmlProject->setAttribute('name', $name);
}
$xmlCoverage->appendChild($xmlProject);
$packages = [];
$report = $coverage->getReport();
foreach ($report as $item) {
if (!$item instanceof File) {
continue;
}
/* @var File $item */
$xmlFile = $xmlDocument->createElement('file');
$xmlFile->setAttribute('name', $item->getPath());
$classes = $item->getClassesAndTraits();
$coverageData = $item->getCoverageData();
$lines = [];
$namespace = 'global';
foreach ($classes as $className => $class) {
$classStatements = 0;
$coveredClassStatements = 0;
$coveredMethods = 0;
$classMethods = 0;
foreach ($class['methods'] as $methodName => $method) {
if ($method['executableLines'] == 0) {
continue;
}
$classMethods++;
$classStatements += $method['executableLines'];
$coveredClassStatements += $method['executedLines'];
if ($method['coverage'] == 100) {
$coveredMethods++;
}
$methodCount = 0;
foreach (\range($method['startLine'], $method['endLine']) as $line) {
if (isset($coverageData[$line]) && ($coverageData[$line] !== null)) {
$methodCount = \max($methodCount, \count($coverageData[$line]));
}
}
$lines[$method['startLine']] = [
'ccn' => $method['ccn'],
'count' => $methodCount,
'crap' => $method['crap'],
'type' => 'method',
'visibility' => $method['visibility'],
'name' => $methodName,
];
}
if (!empty($class['package']['namespace'])) {
$namespace = $class['package']['namespace'];
}
$xmlClass = $xmlDocument->createElement('class');
$xmlClass->setAttribute('name', $className);
$xmlClass->setAttribute('namespace', $namespace);
if (!empty($class['package']['fullPackage'])) {
$xmlClass->setAttribute(
'fullPackage',
$class['package']['fullPackage']
);
}
if (!empty($class['package']['category'])) {
$xmlClass->setAttribute(
'category',
$class['package']['category']
);
}
if (!empty($class['package']['package'])) {
$xmlClass->setAttribute(
'package',
$class['package']['package']
);
}
if (!empty($class['package']['subpackage'])) {
$xmlClass->setAttribute(
'subpackage',
$class['package']['subpackage']
);
}
$xmlFile->appendChild($xmlClass);
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('complexity', (string) $class['ccn']);
$xmlMetrics->setAttribute('methods', (string) $classMethods);
$xmlMetrics->setAttribute('coveredmethods', (string) $coveredMethods);
$xmlMetrics->setAttribute('conditionals', '0');
$xmlMetrics->setAttribute('coveredconditionals', '0');
$xmlMetrics->setAttribute('statements', (string) $classStatements);
$xmlMetrics->setAttribute('coveredstatements', (string) $coveredClassStatements);
$xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements /* + conditionals */));
$xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements /* + coveredconditionals */));
$xmlClass->appendChild($xmlMetrics);
}
foreach ($coverageData as $line => $data) {
if ($data === null || isset($lines[$line])) {
continue;
}
$lines[$line] = [
'count' => \count($data), 'type' => 'stmt',
];
}
\ksort($lines);
foreach ($lines as $line => $data) {
$xmlLine = $xmlDocument->createElement('line');
$xmlLine->setAttribute('num', (string) $line);
$xmlLine->setAttribute('type', $data['type']);
if (isset($data['name'])) {
$xmlLine->setAttribute('name', $data['name']);
}
if (isset($data['visibility'])) {
$xmlLine->setAttribute('visibility', $data['visibility']);
}
if (isset($data['ccn'])) {
$xmlLine->setAttribute('complexity', (string) $data['ccn']);
}
if (isset($data['crap'])) {
$xmlLine->setAttribute('crap', (string) $data['crap']);
}
$xmlLine->setAttribute('count', (string) $data['count']);
$xmlFile->appendChild($xmlLine);
}
$linesOfCode = $item->getLinesOfCode();
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['loc']);
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['ncloc']);
$xmlMetrics->setAttribute('classes', (string) $item->getNumClassesAndTraits());
$xmlMetrics->setAttribute('methods', (string) $item->getNumMethods());
$xmlMetrics->setAttribute('coveredmethods', (string) $item->getNumTestedMethods());
$xmlMetrics->setAttribute('conditionals', '0');
$xmlMetrics->setAttribute('coveredconditionals', '0');
$xmlMetrics->setAttribute('statements', (string) $item->getNumExecutableLines());
$xmlMetrics->setAttribute('coveredstatements', (string) $item->getNumExecutedLines());
$xmlMetrics->setAttribute('elements', (string) ($item->getNumMethods() + $item->getNumExecutableLines() /* + conditionals */));
$xmlMetrics->setAttribute('coveredelements', (string) ($item->getNumTestedMethods() + $item->getNumExecutedLines() /* + coveredconditionals */));
$xmlFile->appendChild($xmlMetrics);
if ($namespace === 'global') {
$xmlProject->appendChild($xmlFile);
} else {
if (!isset($packages[$namespace])) {
$packages[$namespace] = $xmlDocument->createElement(
'package'
);
$packages[$namespace]->setAttribute('name', $namespace);
$xmlProject->appendChild($packages[$namespace]);
}
$packages[$namespace]->appendChild($xmlFile);
}
}
$linesOfCode = $report->getLinesOfCode();
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('files', (string) \count($report));
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['loc']);
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['ncloc']);
$xmlMetrics->setAttribute('classes', (string) $report->getNumClassesAndTraits());
$xmlMetrics->setAttribute('methods', (string) $report->getNumMethods());
$xmlMetrics->setAttribute('coveredmethods', (string) $report->getNumTestedMethods());
$xmlMetrics->setAttribute('conditionals', '0');
$xmlMetrics->setAttribute('coveredconditionals', '0');
$xmlMetrics->setAttribute('statements', (string) $report->getNumExecutableLines());
$xmlMetrics->setAttribute('coveredstatements', (string) $report->getNumExecutedLines());
$xmlMetrics->setAttribute('elements', (string) ($report->getNumMethods() + $report->getNumExecutableLines() /* + conditionals */));
$xmlMetrics->setAttribute('coveredelements', (string) ($report->getNumTestedMethods() + $report->getNumExecutedLines() /* + coveredconditionals */));
$xmlProject->appendChild($xmlMetrics);
$buffer = $xmlDocument->saveXML();
if ($target !== null) {
if (!$this->createDirectory(\dirname($target))) {
throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target)));
}
if (@\file_put_contents($target, $buffer) === false) {
throw new RuntimeException(
\sprintf(
'Could not write to "%s',
$target
)
);
}
}
return $buffer;
}
private function createDirectory(string $directory): bool
{
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Report;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\RuntimeException;
final class Crap4j
{
/**
* @var int
*/
private $threshold;
public function __construct(int $threshold = 30)
{
$this->threshold = $threshold;
}
/**
* @throws \RuntimeException
*/
public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string
{
$document = new \DOMDocument('1.0', 'UTF-8');
$document->formatOutput = true;
$root = $document->createElement('crap_result');
$document->appendChild($root);
$project = $document->createElement('project', \is_string($name) ? $name : '');
$root->appendChild($project);
$root->appendChild($document->createElement('timestamp', \date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME'])));
$stats = $document->createElement('stats');
$methodsNode = $document->createElement('methods');
$report = $coverage->getReport();
unset($coverage);
$fullMethodCount = 0;
$fullCrapMethodCount = 0;
$fullCrapLoad = 0;
$fullCrap = 0;
foreach ($report as $item) {
$namespace = 'global';
if (!$item instanceof File) {
continue;
}
$file = $document->createElement('file');
$file->setAttribute('name', $item->getPath());
$classes = $item->getClassesAndTraits();
foreach ($classes as $className => $class) {
foreach ($class['methods'] as $methodName => $method) {
$crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']);
$fullCrap += $method['crap'];
$fullCrapLoad += $crapLoad;
$fullMethodCount++;
if ($method['crap'] >= $this->threshold) {
$fullCrapMethodCount++;
}
$methodNode = $document->createElement('method');
if (!empty($class['package']['namespace'])) {
$namespace = $class['package']['namespace'];
}
$methodNode->appendChild($document->createElement('package', $namespace));
$methodNode->appendChild($document->createElement('className', $className));
$methodNode->appendChild($document->createElement('methodName', $methodName));
$methodNode->appendChild($document->createElement('methodSignature', \htmlspecialchars($method['signature'])));
$methodNode->appendChild($document->createElement('fullMethod', \htmlspecialchars($method['signature'])));
$methodNode->appendChild($document->createElement('crap', (string) $this->roundValue($method['crap'])));
$methodNode->appendChild($document->createElement('complexity', (string) $method['ccn']));
$methodNode->appendChild($document->createElement('coverage', (string) $this->roundValue($method['coverage'])));
$methodNode->appendChild($document->createElement('crapLoad', (string) \round($crapLoad)));
$methodsNode->appendChild($methodNode);
}
}
}
$stats->appendChild($document->createElement('name', 'Method Crap Stats'));
$stats->appendChild($document->createElement('methodCount', (string) $fullMethodCount));
$stats->appendChild($document->createElement('crapMethodCount', (string) $fullCrapMethodCount));
$stats->appendChild($document->createElement('crapLoad', (string) \round($fullCrapLoad)));
$stats->appendChild($document->createElement('totalCrap', (string) $fullCrap));
$crapMethodPercent = 0;
if ($fullMethodCount > 0) {
$crapMethodPercent = $this->roundValue((100 * $fullCrapMethodCount) / $fullMethodCount);
}
$stats->appendChild($document->createElement('crapMethodPercent', (string) $crapMethodPercent));
$root->appendChild($stats);
$root->appendChild($methodsNode);
$buffer = $document->saveXML();
if ($target !== null) {
if (!$this->createDirectory(\dirname($target))) {
throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target)));
}
if (@\file_put_contents($target, $buffer) === false) {
throw new RuntimeException(
\sprintf(
'Could not write to "%s',
$target
)
);
}
}
return $buffer;
}
/**
* @param float $crapValue
* @param int $cyclomaticComplexity
* @param float $coveragePercent
*/
private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent): float
{
$crapLoad = 0;
if ($crapValue >= $this->threshold) {
$crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100);
$crapLoad += $cyclomaticComplexity / $this->threshold;
}
return $crapLoad;
}
/**
* @param float $value
*/
private function roundValue($value): float
{
return \round($value, 2);
}
private function createDirectory(string $directory): bool
{
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Report;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util;
/**
* Generates human readable output from a code coverage object.
*
* The output gets put into a text file our written to the CLI.
*/
final class Text
{
/**
* @var string
*/
private const COLOR_GREEN = "\x1b[30;42m";
/**
* @var string
*/
private const COLOR_YELLOW = "\x1b[30;43m";
/**
* @var string
*/
private const COLOR_RED = "\x1b[37;41m";
/**
* @var string
*/
private const COLOR_HEADER = "\x1b[1;37;40m";
/**
* @var string
*/
private const COLOR_RESET = "\x1b[0m";
/**
* @var string
*/
private const COLOR_EOL = "\x1b[2K";
/**
* @var int
*/
private $lowUpperBound;
/**
* @var int
*/
private $highLowerBound;
/**
* @var bool
*/
private $showUncoveredFiles;
/**
* @var bool
*/
private $showOnlySummary;
public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = false, bool $showOnlySummary = false)
{
$this->lowUpperBound = $lowUpperBound;
$this->highLowerBound = $highLowerBound;
$this->showUncoveredFiles = $showUncoveredFiles;
$this->showOnlySummary = $showOnlySummary;
}
public function process(CodeCoverage $coverage, bool $showColors = false): string
{
$output = \PHP_EOL . \PHP_EOL;
$report = $coverage->getReport();
$colors = [
'header' => '',
'classes' => '',
'methods' => '',
'lines' => '',
'reset' => '',
'eol' => '',
];
if ($showColors) {
$colors['classes'] = $this->getCoverageColor(
$report->getNumTestedClassesAndTraits(),
$report->getNumClassesAndTraits()
);
$colors['methods'] = $this->getCoverageColor(
$report->getNumTestedMethods(),
$report->getNumMethods()
);
$colors['lines'] = $this->getCoverageColor(
$report->getNumExecutedLines(),
$report->getNumExecutableLines()
);
$colors['reset'] = self::COLOR_RESET;
$colors['header'] = self::COLOR_HEADER;
$colors['eol'] = self::COLOR_EOL;
}
$classes = \sprintf(
' Classes: %6s (%d/%d)',
Util::percent(
$report->getNumTestedClassesAndTraits(),
$report->getNumClassesAndTraits(),
true
),
$report->getNumTestedClassesAndTraits(),
$report->getNumClassesAndTraits()
);
$methods = \sprintf(
' Methods: %6s (%d/%d)',
Util::percent(
$report->getNumTestedMethods(),
$report->getNumMethods(),
true
),
$report->getNumTestedMethods(),
$report->getNumMethods()
);
$lines = \sprintf(
' Lines: %6s (%d/%d)',
Util::percent(
$report->getNumExecutedLines(),
$report->getNumExecutableLines(),
true
),
$report->getNumExecutedLines(),
$report->getNumExecutableLines()
);
$padding = \max(\array_map('strlen', [$classes, $methods, $lines]));
if ($this->showOnlySummary) {
$title = 'Code Coverage Report Summary:';
$padding = \max($padding, \strlen($title));
$output .= $this->format($colors['header'], $padding, $title);
} else {
$date = \date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']);
$title = 'Code Coverage Report:';
$output .= $this->format($colors['header'], $padding, $title);
$output .= $this->format($colors['header'], $padding, $date);
$output .= $this->format($colors['header'], $padding, '');
$output .= $this->format($colors['header'], $padding, ' Summary:');
}
$output .= $this->format($colors['classes'], $padding, $classes);
$output .= $this->format($colors['methods'], $padding, $methods);
$output .= $this->format($colors['lines'], $padding, $lines);
if ($this->showOnlySummary) {
return $output . \PHP_EOL;
}
$classCoverage = [];
foreach ($report as $item) {
if (!$item instanceof File) {
continue;
}
$classes = $item->getClassesAndTraits();
foreach ($classes as $className => $class) {
$classStatements = 0;
$coveredClassStatements = 0;
$coveredMethods = 0;
$classMethods = 0;
foreach ($class['methods'] as $method) {
if ($method['executableLines'] == 0) {
continue;
}
$classMethods++;
$classStatements += $method['executableLines'];
$coveredClassStatements += $method['executedLines'];
if ($method['coverage'] == 100) {
$coveredMethods++;
}
}
$namespace = '';
if (!empty($class['package']['namespace'])) {
$namespace = '\\' . $class['package']['namespace'] . '::';
} elseif (!empty($class['package']['fullPackage'])) {
$namespace = '@' . $class['package']['fullPackage'] . '::';
}
$classCoverage[$namespace . $className] = [
'namespace' => $namespace,
'className ' => $className,
'methodsCovered' => $coveredMethods,
'methodCount' => $classMethods,
'statementsCovered' => $coveredClassStatements,
'statementCount' => $classStatements,
];
}
}
\ksort($classCoverage);
$methodColor = '';
$linesColor = '';
$resetColor = '';
foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) {
if ($showColors) {
$methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
$linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
$resetColor = $colors['reset'];
}
$output .= \PHP_EOL . $fullQualifiedPath . \PHP_EOL
. ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '
. ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor;
}
}
return $output . \PHP_EOL;
}
private function getCoverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string
{
$coverage = Util::percent(
$numberOfCoveredElements,
$totalNumberOfElements
);
if ($coverage >= $this->highLowerBound) {
return self::COLOR_GREEN;
}
if ($coverage > $this->lowUpperBound) {
return self::COLOR_YELLOW;
}
return self::COLOR_RED;
}
private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision): string
{
$format = '%' . $precision . 's';
return Util::percent(
$numberOfCoveredElements,
$totalNumberOfElements,
true,
true
) .
' (' . \sprintf($format, $numberOfCoveredElements) . '/' .
\sprintf($format, $totalNumberOfElements) . ')';
}
private function format($color, $padding, $string): string
{
$reset = $color ? self::COLOR_RESET : '';
return $color . \str_pad($string, $padding) . $reset . \PHP_EOL;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
/**
* Exception that is raised when code is unintentionally covered.
*/
final class UnintentionallyCoveredCodeException extends RuntimeException
{
/**
* @var array
*/
private $unintentionallyCoveredUnits = [];
public function __construct(array $unintentionallyCoveredUnits)
{
$this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits;
parent::__construct($this->toString());
}
public function getUnintentionallyCoveredUnits(): array
{
return $this->unintentionallyCoveredUnits;
}
private function toString(): string
{
$message = '';
foreach ($this->unintentionallyCoveredUnits as $unit) {
$message .= '- ' . $unit . "\n";
}
return $message;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
/**
* Exception interface for php-code-coverage component.
*/
interface Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
final class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
/**
* @param int $argument
* @param string $type
* @param null|mixed $value
*
* @return InvalidArgumentException
*/
public static function create($argument, $type, $value = null): self
{
$stack = \debug_backtrace(0);
return new self(
\sprintf(
'Argument #%d%sof %s::%s() must be a %s',
$argument,
$value !== null ? ' (' . \gettype($value) . '#' . $value . ')' : ' (No Value) ',
$stack[1]['class'],
$stack[1]['function'],
$type
)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
/**
* Exception that is raised when @covers must be used but is not.
*/
final class MissingCoversAnnotationException extends RuntimeException
{
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
/**
* Exception that is raised when covered code is not executed.
*/
final class CoveredCodeNotExecutedException extends RuntimeException
{
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
class RuntimeException extends \RuntimeException implements Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Driver;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\RuntimeException;
/**
* Driver for Xdebug's code coverage functionality.
*
* @codeCoverageIgnore
*/
final class Xdebug implements Driver
{
/**
* @var array
*/
private $cacheNumLines = [];
/**
* @var Filter
*/
private $filter;
/**
* @throws RuntimeException
*/
public function __construct(Filter $filter = null)
{
if (!\extension_loaded('xdebug')) {
throw new RuntimeException('This driver requires Xdebug');
}
if (!\ini_get('xdebug.coverage_enable')) {
throw new RuntimeException('xdebug.coverage_enable=On has to be set in php.ini');
}
if ($filter === null) {
$filter = new Filter;
}
$this->filter = $filter;
}
/**
* Start collection of code coverage information.
*/
public function start(bool $determineUnusedAndDead = true): void
{
if ($determineUnusedAndDead) {
\xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
} else {
\xdebug_start_code_coverage();
}
}
/**
* Stop collection of code coverage information.
*/
public function stop(): array
{
$data = \xdebug_get_code_coverage();
\xdebug_stop_code_coverage();
return $this->cleanup($data);
}
private function cleanup(array $data): array
{
foreach (\array_keys($data) as $file) {
unset($data[$file][0]);
if (!$this->filter->isFile($file)) {
continue;
}
$numLines = $this->getNumberOfLinesInFile($file);
foreach (\array_keys($data[$file]) as $line) {
if ($line > $numLines) {
unset($data[$file][$line]);
}
}
}
return $data;
}
private function getNumberOfLinesInFile(string $fileName): int
{
if (!isset($this->cacheNumLines[$fileName])) {
$buffer = \file_get_contents($fileName);
$lines = \substr_count($buffer, "\n");
if (\substr($buffer, -1) !== "\n") {
$lines++;
}
$this->cacheNumLines[$fileName] = $lines;
}
return $this->cacheNumLines[$fileName];
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Driver;
/**
* Driver for PCOV code coverage functionality.
*
* @codeCoverageIgnore
*/
final class PCOV implements Driver
{
/**
* Start collection of code coverage information.
*/
public function start(bool $determineUnusedAndDead = true): void
{
\pcov\start();
}
/**
* Stop collection of code coverage information.
*/
public function stop(): array
{
\pcov\stop();
$waiting = \pcov\waiting();
$collect = [];
if ($waiting) {
$collect = \pcov\collect(\pcov\inclusive, $waiting);
\pcov\clear();
}
return $collect;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Driver;
/**
* Interface for code coverage drivers.
*/
interface Driver
{
/**
* @var int
*
* @see http://xdebug.org/docs/code_coverage
*/
public const LINE_EXECUTED = 1;
/**
* @var int
*
* @see http://xdebug.org/docs/code_coverage
*/
public const LINE_NOT_EXECUTED = -1;
/**
* @var int
*
* @see http://xdebug.org/docs/code_coverage
*/
public const LINE_NOT_EXECUTABLE = -2;
/**
* Start collection of code coverage information.
*/
public function start(bool $determineUnusedAndDead = true): void;
/**
* Stop collection of code coverage information.
*/
public function stop(): array;
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Driver;
use SebastianBergmann\CodeCoverage\RuntimeException;
/**
* Driver for PHPDBG's code coverage functionality.
*
* @codeCoverageIgnore
*/
final class PHPDBG implements Driver
{
/**
* @throws RuntimeException
*/
public function __construct()
{
if (\PHP_SAPI !== 'phpdbg') {
throw new RuntimeException(
'This driver requires the PHPDBG SAPI'
);
}
if (!\function_exists('phpdbg_start_oplog')) {
throw new RuntimeException(
'This build of PHPDBG does not support code coverage'
);
}
}
/**
* Start collection of code coverage information.
*/
public function start(bool $determineUnusedAndDead = true): void
{
\phpdbg_start_oplog();
}
/**
* Stop collection of code coverage information.
*/
public function stop(): array
{
static $fetchedLines = [];
$dbgData = \phpdbg_end_oplog();
if ($fetchedLines == []) {
$sourceLines = \phpdbg_get_executable();
} else {
$newFiles = \array_diff(\get_included_files(), \array_keys($fetchedLines));
$sourceLines = [];
if ($newFiles) {
$sourceLines = phpdbg_get_executable(['files' => $newFiles]);
}
}
foreach ($sourceLines as $file => $lines) {
foreach ($lines as $lineNo => $numExecuted) {
$sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED;
}
}
$fetchedLines = \array_merge($fetchedLines, $sourceLines);
return $this->detectExecutedLines($fetchedLines, $dbgData);
}
/**
* Convert phpdbg based data into the format CodeCoverage expects
*/
private function detectExecutedLines(array $sourceLines, array $dbgData): array
{
foreach ($dbgData as $file => $coveredLines) {
foreach ($coveredLines as $lineNo => $numExecuted) {
// phpdbg also reports $lineNo=0 when e.g. exceptions get thrown.
// make sure we only mark lines executed which are actually executable.
if (isset($sourceLines[$file][$lineNo])) {
$sourceLines[$file][$lineNo] = self::LINE_EXECUTED;
}
}
}
return $sourceLines;
}
}
<?php declare(strict_types=1);
/*
* This file is part of the php-code-coverage package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
/**
* Filter for whitelisting of code coverage information.
*/
final class Filter
{
/**
* Source files that are whitelisted.
*
* @var array
*/
private $whitelistedFiles = [];
/**
* Remembers the result of the `is_file()` calls.
*
* @var bool[]
*/
private $isFileCallsCache = [];
/**
* Adds a directory to the whitelist (recursively).
*/
public function addDirectoryToWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void
{
$facade = new FileIteratorFacade;
$files = $facade->getFilesAsArray($directory, $suffix, $prefix);
foreach ($files as $file) {
$this->addFileToWhitelist($file);
}
}
/**
* Adds a file to the whitelist.
*/
public function addFileToWhitelist(string $filename): void
{
$filename = \realpath($filename);
if (!$filename) {
return;
}
$this->whitelistedFiles[$filename] = true;
}
/**
* Adds files to the whitelist.
*
* @param string[] $files
*/
public function addFilesToWhitelist(array $files): void
{
foreach ($files as $file) {
$this->addFileToWhitelist($file);
}
}
/**
* Removes a directory from the whitelist (recursively).
*/
public function removeDirectoryFromWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void
{
$facade = new FileIteratorFacade;
$files = $facade->getFilesAsArray($directory, $suffix, $prefix);
foreach ($files as $file) {
$this->removeFileFromWhitelist($file);
}
}
/**
* Removes a file from the whitelist.
*/
public function removeFileFromWhitelist(string $filename): void
{
$filename = \realpath($filename);
if (!$filename || !isset($this->whitelistedFiles[$filename])) {
return;
}
unset($this->whitelistedFiles[$filename]);
}
/**
* Checks whether a filename is a real filename.
*/
public function isFile(string $filename): bool
{
if (isset($this->isFileCallsCache[$filename])) {
return $this->isFileCallsCache[$filename];
}
if ($filename === '-' ||
\strpos($filename, 'vfs://') === 0 ||
\strpos($filename, 'xdebug://debug-eval') !== false ||
\strpos($filename, 'eval()\'d code') !== false ||
\strpos($filename, 'runtime-created function') !== false ||
\strpos($filename, 'runkit created function') !== false ||
\strpos($filename, 'assert code') !== false ||
\strpos($filename, 'regexp code') !== false ||
\strpos($filename, 'Standard input code') !== false) {
$isFile = false;
} else {
$isFile = \file_exists($filename);
}
$this->isFileCallsCache[$filename] = $isFile;
return $isFile;
}
/**
* Checks whether or not a file is filtered.
*/
public function isFiltered(string $filename): bool
{
if (!$this->isFile($filename)) {
return true;
}
return !isset($this->whitelistedFiles[$filename]);
}
/**
* Returns the list of whitelisted files.
*
* @return string[]
*/
public function getWhitelist(): array
{
return \array_keys($this->whitelistedFiles);
}
/**
* Returns whether this filter has a whitelist.
*/
public function hasWhitelist(): bool
{
return !empty($this->whitelistedFiles);
}
/**
* Returns the whitelisted files.
*
* @return string[]
*/
public function getWhitelistedFiles(): array
{
return $this->whitelistedFiles;
}
/**
* Sets the whitelisted files.
*/
public function setWhitelistedFiles(array $whitelistedFiles): void
{
$this->whitelistedFiles = $whitelistedFiles;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\AfterLastTestHook;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\BeforeFirstTestHook;
use PHPUnit\Runner\DefaultTestResultCache;
use PHPUnit\Runner\Filter\ExcludeGroupFilterIterator;
use PHPUnit\Runner\Filter\Factory;
use PHPUnit\Runner\Filter\IncludeGroupFilterIterator;
use PHPUnit\Runner\Filter\NameFilterIterator;
use PHPUnit\Runner\Hook;
use PHPUnit\Runner\NullTestResultCache;
use PHPUnit\Runner\ResultCacheExtension;
use PHPUnit\Runner\StandardTestSuiteLoader;
use PHPUnit\Runner\TestHook;
use PHPUnit\Runner\TestListenerAdapter;
use PHPUnit\Runner\TestSuiteLoader;
use PHPUnit\Runner\TestSuiteSorter;
use PHPUnit\Runner\Version;
use PHPUnit\Util\Configuration;
use PHPUnit\Util\Filesystem;
use PHPUnit\Util\Log\JUnit;
use PHPUnit\Util\Log\TeamCity;
use PHPUnit\Util\Printer;
use PHPUnit\Util\TestDox\CliTestDoxPrinter;
use PHPUnit\Util\TestDox\HtmlResultPrinter;
use PHPUnit\Util\TestDox\TextResultPrinter;
use PHPUnit\Util\TestDox\XmlResultPrinter;
use PHPUnit\Util\XdebugFilterScriptGenerator;
use ReflectionClass;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter;
use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport;
use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport;
use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport;
use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport;
use SebastianBergmann\CodeCoverage\Report\Text as TextReport;
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Environment\Runtime;
use SebastianBergmann\Invoker\Invoker;
use SebastianBergmann\Timer\Timer;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TestRunner extends BaseTestRunner
{
public const SUCCESS_EXIT = 0;
public const FAILURE_EXIT = 1;
public const EXCEPTION_EXIT = 2;
/**
* @var bool
*/
private static $versionStringPrinted = false;
/**
* @var CodeCoverageFilter
*/
private $codeCoverageFilter;
/**
* @var TestSuiteLoader
*/
private $loader;
/**
* @var ResultPrinter
*/
private $printer;
/**
* @var Runtime
*/
private $runtime;
/**
* @var bool
*/
private $messagePrinted = false;
/**
* @var Hook[]
*/
private $extensions = [];
public function __construct(TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null)
{
if ($filter === null) {
$filter = new CodeCoverageFilter;
}
$this->codeCoverageFilter = $filter;
$this->loader = $loader;
$this->runtime = new Runtime;
}
/**
* @throws \PHPUnit\Runner\Exception
* @throws Exception
*/
public function doRun(Test $suite, array $arguments = [], bool $exit = true): TestResult
{
if (isset($arguments['configuration'])) {
$GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration'];
}
$this->handleConfiguration($arguments);
if (\is_int($arguments['columns']) && $arguments['columns'] < 16) {
$arguments['columns'] = 16;
$tooFewColumnsRequested = true;
}
if (isset($arguments['bootstrap'])) {
$GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap'];
}
if ($suite instanceof TestCase || $suite instanceof TestSuite) {
if ($arguments['backupGlobals'] === true) {
$suite->setBackupGlobals(true);
}
if ($arguments['backupStaticAttributes'] === true) {
$suite->setBackupStaticAttributes(true);
}
if ($arguments['beStrictAboutChangesToGlobalState'] === true) {
$suite->setBeStrictAboutChangesToGlobalState(true);
}
}
if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) {
\mt_srand($arguments['randomOrderSeed']);
}
if ($arguments['cacheResult']) {
if (!isset($arguments['cacheResultFile'])) {
if (isset($arguments['configuration']) && $arguments['configuration'] instanceof Configuration) {
$cacheLocation = $arguments['configuration']->getFilename();
} else {
$cacheLocation = $_SERVER['PHP_SELF'];
}
$arguments['cacheResultFile'] = null;
$cacheResultFile = \realpath($cacheLocation);
if ($cacheResultFile !== false) {
$arguments['cacheResultFile'] = \dirname($cacheResultFile);
}
}
$cache = new DefaultTestResultCache($arguments['cacheResultFile']);
$this->addExtension(new ResultCacheExtension($cache));
}
if ($arguments['executionOrder'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['executionOrderDefects'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['resolveDependencies']) {
$cache = $cache ?? new NullTestResultCache;
$cache->load();
$sorter = new TestSuiteSorter($cache);
$sorter->reorderTestsInSuite($suite, $arguments['executionOrder'], $arguments['resolveDependencies'], $arguments['executionOrderDefects']);
$originalExecutionOrder = $sorter->getOriginalExecutionOrder();
unset($sorter);
}
if (\is_int($arguments['repeat']) && $arguments['repeat'] > 0) {
$_suite = new TestSuite;
/* @noinspection PhpUnusedLocalVariableInspection */
foreach (\range(1, $arguments['repeat']) as $step) {
$_suite->addTest($suite);
}
$suite = $_suite;
unset($_suite);
}
$result = $this->createTestResult();
$listener = new TestListenerAdapter;
$listenerNeeded = false;
foreach ($this->extensions as $extension) {
if ($extension instanceof TestHook) {
$listener->add($extension);
$listenerNeeded = true;
}
}
if ($listenerNeeded) {
$result->addListener($listener);
}
unset($listener, $listenerNeeded);
if (!$arguments['convertDeprecationsToExceptions']) {
$result->convertDeprecationsToExceptions(false);
}
if (!$arguments['convertErrorsToExceptions']) {
$result->convertErrorsToExceptions(false);
}
if (!$arguments['convertNoticesToExceptions']) {
$result->convertNoticesToExceptions(false);
}
if (!$arguments['convertWarningsToExceptions']) {
$result->convertWarningsToExceptions(false);
}
if ($arguments['stopOnError']) {
$result->stopOnError(true);
}
if ($arguments['stopOnFailure']) {
$result->stopOnFailure(true);
}
if ($arguments['stopOnWarning']) {
$result->stopOnWarning(true);
}
if ($arguments['stopOnIncomplete']) {
$result->stopOnIncomplete(true);
}
if ($arguments['stopOnRisky']) {
$result->stopOnRisky(true);
}
if ($arguments['stopOnSkipped']) {
$result->stopOnSkipped(true);
}
if ($arguments['stopOnDefect']) {
$result->stopOnDefect(true);
}
if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) {
$result->setRegisterMockObjectsFromTestArgumentsRecursively(true);
}
if ($this->printer === null) {
if (isset($arguments['printer'])) {
if ($arguments['printer'] instanceof Printer) {
$this->printer = $arguments['printer'];
} elseif (\is_string($arguments['printer']) && \class_exists($arguments['printer'], false)) {
try {
$class = new ReflectionClass($arguments['printer']);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($class->isSubclassOf(ResultPrinter::class)) {
$this->printer = $this->createPrinter($arguments['printer'], $arguments);
}
}
} else {
$this->printer = $this->createPrinter(ResultPrinter::class, $arguments);
}
}
if (isset($originalExecutionOrder) && $this->printer instanceof CliTestDoxPrinter) {
\assert($this->printer instanceof CliTestDoxPrinter);
$this->printer->setOriginalExecutionOrder($originalExecutionOrder);
$this->printer->setShowProgressAnimation(!$arguments['noInteraction']);
}
$this->printer->write(
Version::getVersionString() . "\n"
);
self::$versionStringPrinted = true;
if ($arguments['verbose']) {
$this->writeMessage('Runtime', $this->runtime->getNameWithVersionAndCodeCoverageDriver());
if (isset($arguments['configuration'])) {
$this->writeMessage(
'Configuration',
$arguments['configuration']->getFilename()
);
}
foreach ($arguments['loadedExtensions'] as $extension) {
$this->writeMessage(
'Extension',
$extension
);
}
foreach ($arguments['notLoadedExtensions'] as $extension) {
$this->writeMessage(
'Extension',
$extension
);
}
}
if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) {
$this->writeMessage(
'Random seed',
(string) $arguments['randomOrderSeed']
);
}
if (isset($tooFewColumnsRequested)) {
$this->writeMessage('Error', 'Less than 16 columns requested, number of columns set to 16');
}
if ($this->runtime->discardsComments()) {
$this->writeMessage('Warning', 'opcache.save_comments=0 set; annotations will not work');
}
if (isset($arguments['configuration']) && $arguments['configuration']->hasValidationErrors()) {
$this->write(
"\n Warning - The configuration file did not pass validation!\n The following problems have been detected:\n"
);
foreach ($arguments['configuration']->getValidationErrors() as $line => $errors) {
$this->write(\sprintf("\n Line %d:\n", $line));
foreach ($errors as $msg) {
$this->write(\sprintf(" - %s\n", $msg));
}
}
$this->write("\n Test results may not be as expected.\n\n");
}
if (isset($arguments['conflictBetweenPrinterClassAndTestdox'])) {
$this->writeMessage('Warning', 'Directives printerClass and testdox are mutually exclusive');
}
foreach ($arguments['listeners'] as $listener) {
$result->addListener($listener);
}
$result->addListener($this->printer);
$codeCoverageReports = 0;
if (!isset($arguments['noLogging'])) {
if (isset($arguments['testdoxHTMLFile'])) {
$result->addListener(
new HtmlResultPrinter(
$arguments['testdoxHTMLFile'],
$arguments['testdoxGroups'],
$arguments['testdoxExcludeGroups']
)
);
}
if (isset($arguments['testdoxTextFile'])) {
$result->addListener(
new TextResultPrinter(
$arguments['testdoxTextFile'],
$arguments['testdoxGroups'],
$arguments['testdoxExcludeGroups']
)
);
}
if (isset($arguments['testdoxXMLFile'])) {
$result->addListener(
new XmlResultPrinter(
$arguments['testdoxXMLFile']
)
);
}
if (isset($arguments['teamcityLogfile'])) {
$result->addListener(
new TeamCity($arguments['teamcityLogfile'])
);
}
if (isset($arguments['junitLogfile'])) {
$result->addListener(
new JUnit(
$arguments['junitLogfile'],
$arguments['reportUselessTests']
)
);
}
if (isset($arguments['coverageClover'])) {
$codeCoverageReports++;
}
if (isset($arguments['coverageCrap4J'])) {
$codeCoverageReports++;
}
if (isset($arguments['coverageHtml'])) {
$codeCoverageReports++;
}
if (isset($arguments['coveragePHP'])) {
$codeCoverageReports++;
}
if (isset($arguments['coverageText'])) {
$codeCoverageReports++;
}
if (isset($arguments['coverageXml'])) {
$codeCoverageReports++;
}
}
if (isset($arguments['noCoverage'])) {
$codeCoverageReports = 0;
}
if ($codeCoverageReports > 0 && !$this->runtime->canCollectCodeCoverage()) {
$this->writeMessage('Error', 'No code coverage driver is available');
$codeCoverageReports = 0;
}
if ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) {
$whitelistFromConfigurationFile = false;
$whitelistFromOption = false;
if (isset($arguments['whitelist'])) {
$this->codeCoverageFilter->addDirectoryToWhitelist($arguments['whitelist']);
$whitelistFromOption = true;
}
if (isset($arguments['configuration'])) {
$filterConfiguration = $arguments['configuration']->getFilterConfiguration();
if (!empty($filterConfiguration['whitelist'])) {
$whitelistFromConfigurationFile = true;
}
if (!empty($filterConfiguration['whitelist'])) {
foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) {
$this->codeCoverageFilter->addDirectoryToWhitelist(
$dir['path'],
$dir['suffix'],
$dir['prefix']
);
}
foreach ($filterConfiguration['whitelist']['include']['file'] as $file) {
$this->codeCoverageFilter->addFileToWhitelist($file);
}
foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) {
$this->codeCoverageFilter->removeDirectoryFromWhitelist(
$dir['path'],
$dir['suffix'],
$dir['prefix']
);
}
foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) {
$this->codeCoverageFilter->removeFileFromWhitelist($file);
}
}
}
}
if ($codeCoverageReports > 0) {
$codeCoverage = new CodeCoverage(
null,
$this->codeCoverageFilter
);
$codeCoverage->setUnintentionallyCoveredSubclassesWhitelist(
[Comparator::class]
);
$codeCoverage->setCheckForUnintentionallyCoveredCode(
$arguments['strictCoverage']
);
$codeCoverage->setCheckForMissingCoversAnnotation(
$arguments['strictCoverage']
);
if (isset($arguments['forceCoversAnnotation'])) {
$codeCoverage->setForceCoversAnnotation(
$arguments['forceCoversAnnotation']
);
}
if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) {
$codeCoverage->setIgnoreDeprecatedCode(
$arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']
);
}
if (isset($arguments['disableCodeCoverageIgnore'])) {
$codeCoverage->setDisableIgnoredLines(true);
}
if (!empty($filterConfiguration['whitelist'])) {
$codeCoverage->setAddUncoveredFilesFromWhitelist(
$filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist']
);
$codeCoverage->setProcessUncoveredFilesFromWhitelist(
$filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist']
);
}
if (!$this->codeCoverageFilter->hasWhitelist()) {
if (!$whitelistFromConfigurationFile && !$whitelistFromOption) {
$this->writeMessage('Error', 'No whitelist is configured, no code coverage will be generated.');
} else {
$this->writeMessage('Error', 'Incorrect whitelist config, no code coverage will be generated.');
}
$codeCoverageReports = 0;
unset($codeCoverage);
}
}
if (isset($arguments['xdebugFilterFile'], $filterConfiguration)) {
$this->write("\n");
$script = (new XdebugFilterScriptGenerator)->generate($filterConfiguration['whitelist']);
if ($arguments['xdebugFilterFile'] !== 'php://stdout' && $arguments['xdebugFilterFile'] !== 'php://stderr' && !Filesystem::createDirectory(\dirname($arguments['xdebugFilterFile']))) {
$this->write(\sprintf('Cannot write Xdebug filter script to %s ' . \PHP_EOL, $arguments['xdebugFilterFile']));
exit(self::EXCEPTION_EXIT);
}
\file_put_contents($arguments['xdebugFilterFile'], $script);
$this->write(\sprintf('Wrote Xdebug filter script to %s ' . \PHP_EOL, $arguments['xdebugFilterFile']));
exit(self::SUCCESS_EXIT);
}
$this->printer->write("\n");
if (isset($codeCoverage)) {
$result->setCodeCoverage($codeCoverage);
if ($codeCoverageReports > 1 && isset($arguments['cacheTokens'])) {
$codeCoverage->setCacheTokens($arguments['cacheTokens']);
}
}
$result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']);
$result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']);
$result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']);
$result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']);
if ($arguments['enforceTimeLimit'] === true) {
if (!\class_exists(Invoker::class)) {
$this->writeMessage('Error', 'Package phpunit/php-invoker is required for enforcing time limits');
}
if (!\extension_loaded('pcntl') || \strpos(\ini_get('disable_functions'), 'pcntl') !== false) {
$this->writeMessage('Error', 'PHP extension pcntl is required for enforcing time limits');
}
}
$result->enforceTimeLimit($arguments['enforceTimeLimit']);
$result->setDefaultTimeLimit($arguments['defaultTimeLimit']);
$result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']);
$result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']);
$result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']);
if ($suite instanceof TestSuite) {
$this->processSuiteFilters($suite, $arguments);
$suite->setRunTestInSeparateProcess($arguments['processIsolation']);
}
foreach ($this->extensions as $extension) {
if ($extension instanceof BeforeFirstTestHook) {
$extension->executeBeforeFirstTest();
}
}
$suite->run($result);
foreach ($this->extensions as $extension) {
if ($extension instanceof AfterLastTestHook) {
$extension->executeAfterLastTest();
}
}
$result->flushListeners();
if ($this->printer instanceof ResultPrinter) {
$this->printer->printResult($result);
}
if (isset($codeCoverage)) {
if (isset($arguments['coverageClover'])) {
$this->codeCoverageGenerationStart('Clover XML');
try {
$writer = new CloverReport;
$writer->process($codeCoverage, $arguments['coverageClover']);
$this->codeCoverageGenerationSucceeded();
unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}
if (isset($arguments['coverageCrap4J'])) {
$this->codeCoverageGenerationStart('Crap4J XML');
try {
$writer = new Crap4jReport($arguments['crap4jThreshold']);
$writer->process($codeCoverage, $arguments['coverageCrap4J']);
$this->codeCoverageGenerationSucceeded();
unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}
if (isset($arguments['coverageHtml'])) {
$this->codeCoverageGenerationStart('HTML');
try {
$writer = new HtmlReport(
$arguments['reportLowUpperBound'],
$arguments['reportHighLowerBound'],
\sprintf(
' and <a href="https://phpunit.de/">PHPUnit %s</a>',
Version::id()
)
);
$writer->process($codeCoverage, $arguments['coverageHtml']);
$this->codeCoverageGenerationSucceeded();
unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}
if (isset($arguments['coveragePHP'])) {
$this->codeCoverageGenerationStart('PHP');
try {
$writer = new PhpReport;
$writer->process($codeCoverage, $arguments['coveragePHP']);
$this->codeCoverageGenerationSucceeded();
unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}
if (isset($arguments['coverageText'])) {
if ($arguments['coverageText'] === 'php://stdout') {
$outputStream = $this->printer;
$colors = $arguments['colors'] && $arguments['colors'] !== ResultPrinter::COLOR_NEVER;
} else {
$outputStream = new Printer($arguments['coverageText']);
$colors = false;
}
$processor = new TextReport(
$arguments['reportLowUpperBound'],
$arguments['reportHighLowerBound'],
$arguments['coverageTextShowUncoveredFiles'],
$arguments['coverageTextShowOnlySummary']
);
$outputStream->write(
$processor->process($codeCoverage, $colors)
);
}
if (isset($arguments['coverageXml'])) {
$this->codeCoverageGenerationStart('PHPUnit XML');
try {
$writer = new XmlReport(Version::id());
$writer->process($codeCoverage, $arguments['coverageXml']);
$this->codeCoverageGenerationSucceeded();
unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}
}
if ($exit) {
if ($result->wasSuccessfulIgnoringWarnings()) {
if ($arguments['failOnRisky'] && !$result->allHarmless()) {
exit(self::FAILURE_EXIT);
}
if ($arguments['failOnWarning'] && $result->warningCount() > 0) {
exit(self::FAILURE_EXIT);
}
exit(self::SUCCESS_EXIT);
}
if ($result->errorCount() > 0) {
exit(self::EXCEPTION_EXIT);
}
if ($result->failureCount() > 0) {
exit(self::FAILURE_EXIT);
}
}
return $result;
}
public function setPrinter(ResultPrinter $resultPrinter): void
{
$this->printer = $resultPrinter;
}
/**
* Returns the loader to be used.
*/
public function getLoader(): TestSuiteLoader
{
if ($this->loader === null) {
$this->loader = new StandardTestSuiteLoader;
}
return $this->loader;
}
public function addExtension(Hook $extension): void
{
$this->extensions[] = $extension;
}
/**
* Override to define how to handle a failed loading of
* a test suite.
*/
protected function runFailed(string $message): void
{
$this->write($message . \PHP_EOL);
exit(self::FAILURE_EXIT);
}
private function createTestResult(): TestResult
{
return new TestResult;
}
private function write(string $buffer): void
{
if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg') {
$buffer = \htmlspecialchars($buffer);
}
if ($this->printer !== null) {
$this->printer->write($buffer);
} else {
print $buffer;
}
}
/**
* @throws Exception
*/
private function handleConfiguration(array &$arguments): void
{
if (isset($arguments['configuration']) &&
!$arguments['configuration'] instanceof Configuration) {
$arguments['configuration'] = Configuration::getInstance(
$arguments['configuration']
);
}
$arguments['debug'] = $arguments['debug'] ?? false;
$arguments['filter'] = $arguments['filter'] ?? false;
$arguments['listeners'] = $arguments['listeners'] ?? [];
if (isset($arguments['configuration'])) {
$arguments['configuration']->handlePHPConfiguration();
$phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration();
if (isset($phpunitConfiguration['backupGlobals']) && !isset($arguments['backupGlobals'])) {
$arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals'];
}
if (isset($phpunitConfiguration['backupStaticAttributes']) && !isset($arguments['backupStaticAttributes'])) {
$arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes'];
}
if (isset($phpunitConfiguration['beStrictAboutChangesToGlobalState']) && !isset($arguments['beStrictAboutChangesToGlobalState'])) {
$arguments['beStrictAboutChangesToGlobalState'] = $phpunitConfiguration['beStrictAboutChangesToGlobalState'];
}
if (isset($phpunitConfiguration['bootstrap']) && !isset($arguments['bootstrap'])) {
$arguments['bootstrap'] = $phpunitConfiguration['bootstrap'];
}
if (isset($phpunitConfiguration['cacheResult']) && !isset($arguments['cacheResult'])) {
$arguments['cacheResult'] = $phpunitConfiguration['cacheResult'];
}
if (isset($phpunitConfiguration['cacheResultFile']) && !isset($arguments['cacheResultFile'])) {
$arguments['cacheResultFile'] = $phpunitConfiguration['cacheResultFile'];
}
if (isset($phpunitConfiguration['cacheTokens']) && !isset($arguments['cacheTokens'])) {
$arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens'];
}
if (isset($phpunitConfiguration['cacheTokens']) && !isset($arguments['cacheTokens'])) {
$arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens'];
}
if (isset($phpunitConfiguration['colors']) && !isset($arguments['colors'])) {
$arguments['colors'] = $phpunitConfiguration['colors'];
}
if (isset($phpunitConfiguration['convertDeprecationsToExceptions']) && !isset($arguments['convertDeprecationsToExceptions'])) {
$arguments['convertDeprecationsToExceptions'] = $phpunitConfiguration['convertDeprecationsToExceptions'];
}
if (isset($phpunitConfiguration['convertErrorsToExceptions']) && !isset($arguments['convertErrorsToExceptions'])) {
$arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions'];
}
if (isset($phpunitConfiguration['convertNoticesToExceptions']) && !isset($arguments['convertNoticesToExceptions'])) {
$arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions'];
}
if (isset($phpunitConfiguration['convertWarningsToExceptions']) && !isset($arguments['convertWarningsToExceptions'])) {
$arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions'];
}
if (isset($phpunitConfiguration['processIsolation']) && !isset($arguments['processIsolation'])) {
$arguments['processIsolation'] = $phpunitConfiguration['processIsolation'];
}
if (isset($phpunitConfiguration['stopOnDefect']) && !isset($arguments['stopOnDefect'])) {
$arguments['stopOnDefect'] = $phpunitConfiguration['stopOnDefect'];
}
if (isset($phpunitConfiguration['stopOnError']) && !isset($arguments['stopOnError'])) {
$arguments['stopOnError'] = $phpunitConfiguration['stopOnError'];
}
if (isset($phpunitConfiguration['stopOnFailure']) && !isset($arguments['stopOnFailure'])) {
$arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure'];
}
if (isset($phpunitConfiguration['stopOnWarning']) && !isset($arguments['stopOnWarning'])) {
$arguments['stopOnWarning'] = $phpunitConfiguration['stopOnWarning'];
}
if (isset($phpunitConfiguration['stopOnIncomplete']) && !isset($arguments['stopOnIncomplete'])) {
$arguments['stopOnIncomplete'] = $phpunitConfiguration['stopOnIncomplete'];
}
if (isset($phpunitConfiguration['stopOnRisky']) && !isset($arguments['stopOnRisky'])) {
$arguments['stopOnRisky'] = $phpunitConfiguration['stopOnRisky'];
}
if (isset($phpunitConfiguration['stopOnSkipped']) && !isset($arguments['stopOnSkipped'])) {
$arguments['stopOnSkipped'] = $phpunitConfiguration['stopOnSkipped'];
}
if (isset($phpunitConfiguration['failOnWarning']) && !isset($arguments['failOnWarning'])) {
$arguments['failOnWarning'] = $phpunitConfiguration['failOnWarning'];
}
if (isset($phpunitConfiguration['failOnRisky']) && !isset($arguments['failOnRisky'])) {
$arguments['failOnRisky'] = $phpunitConfiguration['failOnRisky'];
}
if (isset($phpunitConfiguration['timeoutForSmallTests']) && !isset($arguments['timeoutForSmallTests'])) {
$arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests'];
}
if (isset($phpunitConfiguration['timeoutForMediumTests']) && !isset($arguments['timeoutForMediumTests'])) {
$arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests'];
}
if (isset($phpunitConfiguration['timeoutForLargeTests']) && !isset($arguments['timeoutForLargeTests'])) {
$arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests'];
}
if (isset($phpunitConfiguration['reportUselessTests']) && !isset($arguments['reportUselessTests'])) {
$arguments['reportUselessTests'] = $phpunitConfiguration['reportUselessTests'];
}
if (isset($phpunitConfiguration['strictCoverage']) && !isset($arguments['strictCoverage'])) {
$arguments['strictCoverage'] = $phpunitConfiguration['strictCoverage'];
}
if (isset($phpunitConfiguration['ignoreDeprecatedCodeUnitsFromCodeCoverage']) && !isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) {
$arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $phpunitConfiguration['ignoreDeprecatedCodeUnitsFromCodeCoverage'];
}
if (isset($phpunitConfiguration['disallowTestOutput']) && !isset($arguments['disallowTestOutput'])) {
$arguments['disallowTestOutput'] = $phpunitConfiguration['disallowTestOutput'];
}
if (isset($phpunitConfiguration['defaultTimeLimit']) && !isset($arguments['defaultTimeLimit'])) {
$arguments['defaultTimeLimit'] = $phpunitConfiguration['defaultTimeLimit'];
}
if (isset($phpunitConfiguration['enforceTimeLimit']) && !isset($arguments['enforceTimeLimit'])) {
$arguments['enforceTimeLimit'] = $phpunitConfiguration['enforceTimeLimit'];
}
if (isset($phpunitConfiguration['disallowTodoAnnotatedTests']) && !isset($arguments['disallowTodoAnnotatedTests'])) {
$arguments['disallowTodoAnnotatedTests'] = $phpunitConfiguration['disallowTodoAnnotatedTests'];
}
if (isset($phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']) && !isset($arguments['beStrictAboutResourceUsageDuringSmallTests'])) {
$arguments['beStrictAboutResourceUsageDuringSmallTests'] = $phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests'];
}
if (isset($phpunitConfiguration['verbose']) && !isset($arguments['verbose'])) {
$arguments['verbose'] = $phpunitConfiguration['verbose'];
}
if (isset($phpunitConfiguration['reverseDefectList']) && !isset($arguments['reverseList'])) {
$arguments['reverseList'] = $phpunitConfiguration['reverseDefectList'];
}
if (isset($phpunitConfiguration['forceCoversAnnotation']) && !isset($arguments['forceCoversAnnotation'])) {
$arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation'];
}
if (isset($phpunitConfiguration['disableCodeCoverageIgnore']) && !isset($arguments['disableCodeCoverageIgnore'])) {
$arguments['disableCodeCoverageIgnore'] = $phpunitConfiguration['disableCodeCoverageIgnore'];
}
if (isset($phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']) && !isset($arguments['registerMockObjectsFromTestArgumentsRecursively'])) {
$arguments['registerMockObjectsFromTestArgumentsRecursively'] = $phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively'];
}
if (isset($phpunitConfiguration['executionOrder']) && !isset($arguments['executionOrder'])) {
$arguments['executionOrder'] = $phpunitConfiguration['executionOrder'];
}
if (isset($phpunitConfiguration['executionOrderDefects']) && !isset($arguments['executionOrderDefects'])) {
$arguments['executionOrderDefects'] = $phpunitConfiguration['executionOrderDefects'];
}
if (isset($phpunitConfiguration['resolveDependencies']) && !isset($arguments['resolveDependencies'])) {
$arguments['resolveDependencies'] = $phpunitConfiguration['resolveDependencies'];
}
if (isset($phpunitConfiguration['noInteraction']) && !isset($arguments['noInteraction'])) {
$arguments['noInteraction'] = $phpunitConfiguration['noInteraction'];
}
if (isset($phpunitConfiguration['conflictBetweenPrinterClassAndTestdox'])) {
$arguments['conflictBetweenPrinterClassAndTestdox'] = true;
}
$groupCliArgs = [];
if (!empty($arguments['groups'])) {
$groupCliArgs = $arguments['groups'];
}
$groupConfiguration = $arguments['configuration']->getGroupConfiguration();
if (!empty($groupConfiguration['include']) && !isset($arguments['groups'])) {
$arguments['groups'] = $groupConfiguration['include'];
}
if (!empty($groupConfiguration['exclude']) && !isset($arguments['excludeGroups'])) {
$arguments['excludeGroups'] = \array_diff($groupConfiguration['exclude'], $groupCliArgs);
}
foreach ($arguments['configuration']->getExtensionConfiguration() as $extension) {
if ($extension['file'] !== '' && !\class_exists($extension['class'], false)) {
require_once $extension['file'];
}
if (!\class_exists($extension['class'])) {
throw new Exception(
\sprintf(
'Class "%s" does not exist',
$extension['class']
)
);
}
try {
$extensionClass = new ReflectionClass($extension['class']);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$extensionClass->implementsInterface(Hook::class)) {
throw new Exception(
\sprintf(
'Class "%s" does not implement a PHPUnit\Runner\Hook interface',
$extension['class']
)
);
}
if (\count($extension['arguments']) === 0) {
$extensionObject = $extensionClass->newInstance();
} else {
$extensionObject = $extensionClass->newInstanceArgs(
$extension['arguments']
);
}
\assert($extensionObject instanceof Hook);
$this->addExtension($extensionObject);
}
foreach ($arguments['configuration']->getListenerConfiguration() as $listener) {
if ($listener['file'] !== '' && !\class_exists($listener['class'], false)) {
require_once $listener['file'];
}
if (!\class_exists($listener['class'])) {
throw new Exception(
\sprintf(
'Class "%s" does not exist',
$listener['class']
)
);
}
try {
$listenerClass = new ReflectionClass($listener['class']);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$listenerClass->implementsInterface(TestListener::class)) {
throw new Exception(
\sprintf(
'Class "%s" does not implement the PHPUnit\Framework\TestListener interface',
$listener['class']
)
);
}
if (\count($listener['arguments']) === 0) {
$listener = new $listener['class'];
} else {
$listener = $listenerClass->newInstanceArgs(
$listener['arguments']
);
}
$arguments['listeners'][] = $listener;
}
$loggingConfiguration = $arguments['configuration']->getLoggingConfiguration();
if (isset($loggingConfiguration['coverage-clover']) && !isset($arguments['coverageClover'])) {
$arguments['coverageClover'] = $loggingConfiguration['coverage-clover'];
}
if (isset($loggingConfiguration['coverage-crap4j']) && !isset($arguments['coverageCrap4J'])) {
$arguments['coverageCrap4J'] = $loggingConfiguration['coverage-crap4j'];
if (isset($loggingConfiguration['crap4jThreshold']) && !isset($arguments['crap4jThreshold'])) {
$arguments['crap4jThreshold'] = $loggingConfiguration['crap4jThreshold'];
}
}
if (isset($loggingConfiguration['coverage-html']) && !isset($arguments['coverageHtml'])) {
if (isset($loggingConfiguration['lowUpperBound']) && !isset($arguments['reportLowUpperBound'])) {
$arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound'];
}
if (isset($loggingConfiguration['highLowerBound']) && !isset($arguments['reportHighLowerBound'])) {
$arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound'];
}
$arguments['coverageHtml'] = $loggingConfiguration['coverage-html'];
}
if (isset($loggingConfiguration['coverage-php']) && !isset($arguments['coveragePHP'])) {
$arguments['coveragePHP'] = $loggingConfiguration['coverage-php'];
}
if (isset($loggingConfiguration['coverage-text']) && !isset($arguments['coverageText'])) {
$arguments['coverageText'] = $loggingConfiguration['coverage-text'];
$arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles'] ?? false;
$arguments['coverageTextShowOnlySummary'] = $loggingConfiguration['coverageTextShowOnlySummary'] ?? false;
}
if (isset($loggingConfiguration['coverage-xml']) && !isset($arguments['coverageXml'])) {
$arguments['coverageXml'] = $loggingConfiguration['coverage-xml'];
}
if (isset($loggingConfiguration['plain'])) {
$arguments['listeners'][] = new ResultPrinter(
$loggingConfiguration['plain'],
true
);
}
if (isset($loggingConfiguration['teamcity']) && !isset($arguments['teamcityLogfile'])) {
$arguments['teamcityLogfile'] = $loggingConfiguration['teamcity'];
}
if (isset($loggingConfiguration['junit']) && !isset($arguments['junitLogfile'])) {
$arguments['junitLogfile'] = $loggingConfiguration['junit'];
}
if (isset($loggingConfiguration['testdox-html']) && !isset($arguments['testdoxHTMLFile'])) {
$arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html'];
}
if (isset($loggingConfiguration['testdox-text']) && !isset($arguments['testdoxTextFile'])) {
$arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text'];
}
if (isset($loggingConfiguration['testdox-xml']) && !isset($arguments['testdoxXMLFile'])) {
$arguments['testdoxXMLFile'] = $loggingConfiguration['testdox-xml'];
}
$testdoxGroupConfiguration = $arguments['configuration']->getTestdoxGroupConfiguration();
if (isset($testdoxGroupConfiguration['include']) &&
!isset($arguments['testdoxGroups'])) {
$arguments['testdoxGroups'] = $testdoxGroupConfiguration['include'];
}
if (isset($testdoxGroupConfiguration['exclude']) &&
!isset($arguments['testdoxExcludeGroups'])) {
$arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration['exclude'];
}
}
$arguments['addUncoveredFilesFromWhitelist'] = $arguments['addUncoveredFilesFromWhitelist'] ?? true;
$arguments['backupGlobals'] = $arguments['backupGlobals'] ?? null;
$arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? null;
$arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? null;
$arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? false;
$arguments['cacheResult'] = $arguments['cacheResult'] ?? true;
$arguments['cacheTokens'] = $arguments['cacheTokens'] ?? false;
$arguments['colors'] = $arguments['colors'] ?? ResultPrinter::COLOR_DEFAULT;
$arguments['columns'] = $arguments['columns'] ?? 80;
$arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? true;
$arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? true;
$arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? true;
$arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? true;
$arguments['crap4jThreshold'] = $arguments['crap4jThreshold'] ?? 30;
$arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? false;
$arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? false;
$arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? 0;
$arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? false;
$arguments['excludeGroups'] = $arguments['excludeGroups'] ?? [];
$arguments['executionOrder'] = $arguments['executionOrder'] ?? TestSuiteSorter::ORDER_DEFAULT;
$arguments['executionOrderDefects'] = $arguments['executionOrderDefects'] ?? TestSuiteSorter::ORDER_DEFAULT;
$arguments['failOnRisky'] = $arguments['failOnRisky'] ?? false;
$arguments['failOnWarning'] = $arguments['failOnWarning'] ?? false;
$arguments['groups'] = $arguments['groups'] ?? [];
$arguments['noInteraction'] = $arguments['noInteraction'] ?? false;
$arguments['processIsolation'] = $arguments['processIsolation'] ?? false;
$arguments['processUncoveredFilesFromWhitelist'] = $arguments['processUncoveredFilesFromWhitelist'] ?? false;
$arguments['randomOrderSeed'] = $arguments['randomOrderSeed'] ?? \time();
$arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? false;
$arguments['repeat'] = $arguments['repeat'] ?? false;
$arguments['reportHighLowerBound'] = $arguments['reportHighLowerBound'] ?? 90;
$arguments['reportLowUpperBound'] = $arguments['reportLowUpperBound'] ?? 50;
$arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? true;
$arguments['reverseList'] = $arguments['reverseList'] ?? false;
$arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? true;
$arguments['stopOnError'] = $arguments['stopOnError'] ?? false;
$arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? false;
$arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? false;
$arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? false;
$arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? false;
$arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? false;
$arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? false;
$arguments['strictCoverage'] = $arguments['strictCoverage'] ?? false;
$arguments['testdoxExcludeGroups'] = $arguments['testdoxExcludeGroups'] ?? [];
$arguments['testdoxGroups'] = $arguments['testdoxGroups'] ?? [];
$arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? 60;
$arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? 10;
$arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? 1;
$arguments['verbose'] = $arguments['verbose'] ?? false;
}
private function processSuiteFilters(TestSuite $suite, array $arguments): void
{
if (!$arguments['filter'] &&
empty($arguments['groups']) &&
empty($arguments['excludeGroups'])) {
return;
}
$filterFactory = new Factory;
if (!empty($arguments['excludeGroups'])) {
$filterFactory->addFilter(
new ReflectionClass(ExcludeGroupFilterIterator::class),
$arguments['excludeGroups']
);
}
if (!empty($arguments['groups'])) {
$filterFactory->addFilter(
new ReflectionClass(IncludeGroupFilterIterator::class),
$arguments['groups']
);
}
if ($arguments['filter']) {
$filterFactory->addFilter(
new ReflectionClass(NameFilterIterator::class),
$arguments['filter']
);
}
$suite->injectFilter($filterFactory);
}
private function writeMessage(string $type, string $message): void
{
if (!$this->messagePrinted) {
$this->write("\n");
}
$this->write(
\sprintf(
"%-15s%s\n",
$type . ':',
$message
)
);
$this->messagePrinted = true;
}
private function createPrinter(string $class, array $arguments): Printer
{
return new $class(
(isset($arguments['stderr']) && $arguments['stderr'] === true) ? 'php://stderr' : null,
$arguments['verbose'],
$arguments['colors'],
$arguments['debug'],
$arguments['columns'],
$arguments['reverseList']
);
}
private function codeCoverageGenerationStart(string $format): void
{
$this->printer->write(
\sprintf(
"\nGenerating code coverage report in %s format ... ",
$format
)
);
Timer::start();
}
private function codeCoverageGenerationSucceeded(): void
{
$this->printer->write(
\sprintf(
"done [%s]\n",
Timer::secondsToTimeString(Timer::stop())
)
);
}
private function codeCoverageGenerationFailed(\Exception $e): void
{
$this->printer->write(
\sprintf(
"failed [%s]\n%s\n",
Timer::secondsToTimeString(Timer::stop()),
$e->getMessage()
)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI;
use PHPUnit\Util\Color;
use SebastianBergmann\Environment\Console;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Help
{
private const LEFT_MARGIN = ' ';
private const HELP_TEXT = [
'Usage' => [
['text' => 'phpunit [options] UnitTest [UnitTest.php]'],
['text' => 'phpunit [options] <directory>'],
],
'Code Coverage Options' => [
['arg' => '--coverage-clover <file>', 'desc' => 'Generate code coverage report in Clover XML format'],
['arg' => '--coverage-crap4j <file>', 'desc' => 'Generate code coverage report in Crap4J XML format'],
['arg' => '--coverage-html <dir>', 'desc' => 'Generate code coverage report in HTML format'],
['arg' => '--coverage-php <file>', 'desc' => 'Export PHP_CodeCoverage object to file'],
['arg' => '--coverage-text=<file>', 'desc' => 'Generate code coverage report in text format [default: standard output]'],
['arg' => '--coverage-xml <dir>', 'desc' => 'Generate code coverage report in PHPUnit XML format'],
['arg' => '--whitelist <dir>', 'desc' => 'Whitelist <dir> for code coverage analysis'],
['arg' => '--disable-coverage-ignore', 'desc' => 'Disable annotations for ignoring code coverage'],
['arg' => '--no-coverage', 'desc' => 'Ignore code coverage configuration'],
['arg' => '--dump-xdebug-filter <file>', 'desc' => 'Generate script to set Xdebug code coverage filter'],
],
'Logging Options' => [
['arg' => '--log-junit <file>', 'desc' => 'Log test execution in JUnit XML format to file'],
['arg' => '--log-teamcity <file>', 'desc' => 'Log test execution in TeamCity format to file'],
['arg' => '--testdox-html <file>', 'desc' => 'Write agile documentation in HTML format to file'],
['arg' => '--testdox-text <file>', 'desc' => 'Write agile documentation in Text format to file'],
['arg' => '--testdox-xml <file>', 'desc' => 'Write agile documentation in XML format to file'],
['arg' => '--reverse-list', 'desc' => 'Print defects in reverse order'],
],
'Test Selection Options' => [
['arg' => '--filter <pattern>', 'desc' => 'Filter which tests to run'],
['arg' => '--testsuite <name>', 'desc' => 'Filter which testsuite to run'],
['arg' => '--group <name>', 'desc' => 'Only runs tests from the specified group(s)'],
['arg' => '--exclude-group <name>', 'desc' => 'Exclude tests from the specified group(s)'],
['arg' => '--list-groups', 'desc' => 'List available test groups'],
['arg' => '--list-suites', 'desc' => 'List available test suites'],
['arg' => '--list-tests', 'desc' => 'List available tests'],
['arg' => '--list-tests-xml <file>', 'desc' => 'List available tests in XML format'],
['arg' => '--test-suffix <suffixes>', 'desc' => 'Only search for test in files with specified suffix(es). Default: Test.php,.phpt'],
],
'Test Execution Options' => [
['arg' => '--dont-report-useless-tests', 'desc' => 'Do not report tests that do not test anything'],
['arg' => '--strict-coverage', 'desc' => 'Be strict about @covers annotation usage'],
['arg' => '--strict-global-state', 'desc' => 'Be strict about changes to global state'],
['arg' => '--disallow-test-output', 'desc' => 'Be strict about output during tests'],
['arg' => '--disallow-resource-usage', 'desc' => 'Be strict about resource usage during small tests'],
['arg' => '--enforce-time-limit', 'desc' => 'Enforce time limit based on test size'],
['arg' => '--default-time-limit=<sec>', 'desc' => 'Timeout in seconds for tests without @small, @medium or @large'],
['arg' => '--disallow-todo-tests', 'desc' => 'Disallow @todo-annotated tests'],
['spacer' => ''],
['arg' => '--process-isolation', 'desc' => 'Run each test in a separate PHP process'],
['arg' => '--globals-backup', 'desc' => 'Backup and restore $GLOBALS for each test'],
['arg' => '--static-backup', 'desc' => 'Backup and restore static attributes for each test'],
['spacer' => ''],
['arg' => '--colors=<flag>', 'desc' => 'Use colors in output ("never", "auto" or "always")'],
['arg' => '--columns <n>', 'desc' => 'Number of columns to use for progress output'],
['arg' => '--columns max', 'desc' => 'Use maximum number of columns for progress output'],
['arg' => '--stderr', 'desc' => 'Write to STDERR instead of STDOUT'],
['arg' => '--stop-on-defect', 'desc' => 'Stop execution upon first not-passed test'],
['arg' => '--stop-on-error', 'desc' => 'Stop execution upon first error'],
['arg' => '--stop-on-failure', 'desc' => 'Stop execution upon first error or failure'],
['arg' => '--stop-on-warning', 'desc' => 'Stop execution upon first warning'],
['arg' => '--stop-on-risky', 'desc' => 'Stop execution upon first risky test'],
['arg' => '--stop-on-skipped', 'desc' => 'Stop execution upon first skipped test'],
['arg' => '--stop-on-incomplete', 'desc' => 'Stop execution upon first incomplete test'],
['arg' => '--fail-on-warning', 'desc' => 'Treat tests with warnings as failures'],
['arg' => '--fail-on-risky', 'desc' => 'Treat risky tests as failures'],
['arg' => '-v|--verbose', 'desc' => 'Output more verbose information'],
['arg' => '--debug', 'desc' => 'Display debugging information'],
['spacer' => ''],
['arg' => '--loader <loader>', 'desc' => 'TestSuiteLoader implementation to use'],
['arg' => '--repeat <times>', 'desc' => 'Runs the test(s) repeatedly'],
['arg' => '--teamcity', 'desc' => 'Report test execution progress in TeamCity format'],
['arg' => '--testdox', 'desc' => 'Report test execution progress in TestDox format'],
['arg' => '--testdox-group', 'desc' => 'Only include tests from the specified group(s)'],
['arg' => '--testdox-exclude-group', 'desc' => 'Exclude tests from the specified group(s)'],
['arg' => '--no-interaction', 'desc' => 'Disable TestDox progress animation'],
['arg' => '--printer <printer>', 'desc' => 'TestListener implementation to use'],
['spacer' => ''],
['arg' => '--order-by=<order>', 'desc' => 'Run tests in order: default|defects|duration|no-depends|random|reverse|size'],
['arg' => '--random-order-seed=<N>', 'desc' => 'Use a specific random seed <N> for random order'],
['arg' => '--cache-result', 'desc' => 'Write test results to cache file'],
['arg' => '--do-not-cache-result', 'desc' => 'Do not write test results to cache file'],
],
'Configuration Options' => [
['arg' => '--prepend <file>', 'desc' => 'A PHP script that is included as early as possible'],
['arg' => '--bootstrap <file>', 'desc' => 'A PHP script that is included before the tests run'],
['arg' => '-c|--configuration <file>', 'desc' => 'Read configuration from XML file'],
['arg' => '--no-configuration', 'desc' => 'Ignore default configuration file (phpunit.xml)'],
['arg' => '--no-logging', 'desc' => 'Ignore logging configuration'],
['arg' => '--no-extensions', 'desc' => 'Do not load PHPUnit extensions'],
['arg' => '--include-path <path(s)>', 'desc' => 'Prepend PHP\'s include_path with given path(s)'],
['arg' => '-d <key[=value]>', 'desc' => 'Sets a php.ini value'],
['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'],
['arg' => '--cache-result-file=<file>', 'desc' => 'Specify result cache path and filename'],
],
'Miscellaneous Options' => [
['arg' => '-h|--help', 'desc' => 'Prints this usage information'],
['arg' => '--version', 'desc' => 'Prints the version and exits'],
['arg' => '--atleast-version <min>', 'desc' => 'Checks that version is greater than min and exits'],
['arg' => '--check-version', 'desc' => 'Check whether PHPUnit is the latest version'],
],
];
/**
* @var int Number of columns required to write the longest option name to the console
*/
private $maxArgLength = 0;
/**
* @var int Number of columns left for the description field after padding and option
*/
private $maxDescLength;
/**
* @var bool Use color highlights for sections, options and parameters
*/
private $hasColor = false;
public function __construct(?int $width = null, ?bool $withColor = null)
{
if ($width === null) {
$width = (new Console)->getNumberOfColumns();
}
if ($withColor === null) {
$this->hasColor = (new Console)->hasColorSupport();
} else {
$this->hasColor = $withColor;
}
foreach (self::HELP_TEXT as $section => $options) {
foreach ($options as $option) {
if (isset($option['arg'])) {
$this->maxArgLength = \max($this->maxArgLength, isset($option['arg']) ? \strlen($option['arg']) : 0);
}
}
}
$this->maxDescLength = $width - $this->maxArgLength - 4;
}
/**
* Write the help file to the CLI, adapting width and colors to the console
*/
public function writeToConsole(): void
{
if ($this->hasColor) {
$this->writeWithColor();
} else {
$this->writePlaintext();
}
}
private function writePlaintext(): void
{
foreach (self::HELP_TEXT as $section => $options) {
print "$section:" . \PHP_EOL;
if ($section !== 'Usage') {
print \PHP_EOL;
}
foreach ($options as $option) {
if (isset($option['spacer'])) {
print \PHP_EOL;
}
if (isset($option['text'])) {
print self::LEFT_MARGIN . $option['text'] . \PHP_EOL;
}
if (isset($option['arg'])) {
$arg = \str_pad($option['arg'], $this->maxArgLength);
print self::LEFT_MARGIN . $arg . ' ' . $option['desc'] . \PHP_EOL;
}
}
print \PHP_EOL;
}
}
private function writeWithColor(): void
{
foreach (self::HELP_TEXT as $section => $options) {
print Color::colorize('fg-yellow', "$section:") . \PHP_EOL;
foreach ($options as $option) {
if (isset($option['spacer'])) {
print \PHP_EOL;
}
if (isset($option['text'])) {
print self::LEFT_MARGIN . $option['text'] . \PHP_EOL;
}
if (isset($option['arg'])) {
$arg = Color::colorize('fg-green', \str_pad($option['arg'], $this->maxArgLength));
$arg = \preg_replace_callback(
'/(<[^>]+>)/',
static function ($matches) {
return Color::colorize('fg-cyan', $matches[0]);
},
$arg
);
$desc = \explode(\PHP_EOL, \wordwrap($option['desc'], $this->maxDescLength, \PHP_EOL));
print self::LEFT_MARGIN . $arg . ' ' . $desc[0] . \PHP_EOL;
for ($i = 1; $i < \count($desc); $i++) {
print \str_repeat(' ', $this->maxArgLength + 3) . $desc[$i] . \PHP_EOL;
}
}
}
print \PHP_EOL;
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI;
use PharIo\Manifest\ApplicationName;
use PharIo\Manifest\Exception as ManifestException;
use PharIo\Manifest\ManifestLoader;
use PharIo\Version\Version as PharIoVersion;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\StandardTestSuiteLoader;
use PHPUnit\Runner\TestSuiteLoader;
use PHPUnit\Runner\TestSuiteSorter;
use PHPUnit\Runner\Version;
use PHPUnit\Util\Configuration;
use PHPUnit\Util\ConfigurationGenerator;
use PHPUnit\Util\FileLoader;
use PHPUnit\Util\Filesystem;
use PHPUnit\Util\Getopt;
use PHPUnit\Util\Log\TeamCity;
use PHPUnit\Util\Printer;
use PHPUnit\Util\TestDox\CliTestDoxPrinter;
use PHPUnit\Util\TextTestListRenderer;
use PHPUnit\Util\XmlTestListRenderer;
use ReflectionClass;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
use Throwable;
/**
* A TestRunner for the Command Line Interface (CLI)
* PHP SAPI Module.
*/
class Command
{
/**
* @var array<string,mixed>
*/
protected $arguments = [
'listGroups' => false,
'listSuites' => false,
'listTests' => false,
'listTestsXml' => false,
'loader' => null,
'useDefaultConfiguration' => true,
'loadedExtensions' => [],
'notLoadedExtensions' => [],
];
/**
* @var array<string,mixed>
*/
protected $options = [];
/**
* @var array<string,mixed>
*/
protected $longOptions = [
'atleast-version=' => null,
'prepend=' => null,
'bootstrap=' => null,
'cache-result' => null,
'do-not-cache-result' => null,
'cache-result-file=' => null,
'check-version' => null,
'colors==' => null,
'columns=' => null,
'configuration=' => null,
'coverage-clover=' => null,
'coverage-crap4j=' => null,
'coverage-html=' => null,
'coverage-php=' => null,
'coverage-text==' => null,
'coverage-xml=' => null,
'debug' => null,
'disallow-test-output' => null,
'disallow-resource-usage' => null,
'disallow-todo-tests' => null,
'default-time-limit=' => null,
'enforce-time-limit' => null,
'exclude-group=' => null,
'filter=' => null,
'generate-configuration' => null,
'globals-backup' => null,
'group=' => null,
'help' => null,
'resolve-dependencies' => null,
'ignore-dependencies' => null,
'include-path=' => null,
'list-groups' => null,
'list-suites' => null,
'list-tests' => null,
'list-tests-xml=' => null,
'loader=' => null,
'log-junit=' => null,
'log-teamcity=' => null,
'no-configuration' => null,
'no-coverage' => null,
'no-logging' => null,
'no-interaction' => null,
'no-extensions' => null,
'order-by=' => null,
'printer=' => null,
'process-isolation' => null,
'repeat=' => null,
'dont-report-useless-tests' => null,
'random-order' => null,
'random-order-seed=' => null,
'reverse-order' => null,
'reverse-list' => null,
'static-backup' => null,
'stderr' => null,
'stop-on-defect' => null,
'stop-on-error' => null,
'stop-on-failure' => null,
'stop-on-warning' => null,
'stop-on-incomplete' => null,
'stop-on-risky' => null,
'stop-on-skipped' => null,
'fail-on-warning' => null,
'fail-on-risky' => null,
'strict-coverage' => null,
'disable-coverage-ignore' => null,
'strict-global-state' => null,
'teamcity' => null,
'testdox' => null,
'testdox-group=' => null,
'testdox-exclude-group=' => null,
'testdox-html=' => null,
'testdox-text=' => null,
'testdox-xml=' => null,
'test-suffix=' => null,
'testsuite=' => null,
'verbose' => null,
'version' => null,
'whitelist=' => null,
'dump-xdebug-filter=' => null,
];
/**
* @var bool
*/
private $versionStringPrinted = false;
/**
* @throws \PHPUnit\Framework\Exception
*/
public static function main(bool $exit = true): int
{
return (new static)->run($_SERVER['argv'], $exit);
}
/**
* @throws Exception
*/
public function run(array $argv, bool $exit = true): int
{
$this->handleArguments($argv);
$runner = $this->createRunner();
if ($this->arguments['test'] instanceof Test) {
$suite = $this->arguments['test'];
} else {
$suite = $runner->getTest(
$this->arguments['test'],
$this->arguments['testFile'],
$this->arguments['testSuffixes']
);
}
if ($this->arguments['listGroups']) {
return $this->handleListGroups($suite, $exit);
}
if ($this->arguments['listSuites']) {
return $this->handleListSuites($exit);
}
if ($this->arguments['listTests']) {
return $this->handleListTests($suite, $exit);
}
if ($this->arguments['listTestsXml']) {
return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit);
}
unset($this->arguments['test'], $this->arguments['testFile']);
try {
$result = $runner->doRun($suite, $this->arguments, $exit);
} catch (Exception $e) {
print $e->getMessage() . \PHP_EOL;
}
$return = TestRunner::FAILURE_EXIT;
if (isset($result) && $result->wasSuccessful()) {
$return = TestRunner::SUCCESS_EXIT;
} elseif (!isset($result) || $result->errorCount() > 0) {
$return = TestRunner::EXCEPTION_EXIT;
}
if ($exit) {
exit($return);
}
return $return;
}
/**
* Create a TestRunner, override in subclasses.
*/
protected function createRunner(): TestRunner
{
return new TestRunner($this->arguments['loader']);
}
/**
* Handles the command-line arguments.
*
* A child class of PHPUnit\TextUI\Command can hook into the argument
* parsing by adding the switch(es) to the $longOptions array and point to a
* callback method that handles the switch(es) in the child class like this
*
* <code>
* <?php
* class MyCommand extends PHPUnit\TextUI\Command
* {
* public function __construct()
* {
* // my-switch won't accept a value, it's an on/off
* $this->longOptions['my-switch'] = 'myHandler';
* // my-secondswitch will accept a value - note the equals sign
* $this->longOptions['my-secondswitch='] = 'myOtherHandler';
* }
*
* // --my-switch -> myHandler()
* protected function myHandler()
* {
* }
*
* // --my-secondswitch foo -> myOtherHandler('foo')
* protected function myOtherHandler ($value)
* {
* }
*
* // You will also need this - the static keyword in the
* // PHPUnit\TextUI\Command will mean that it'll be
* // PHPUnit\TextUI\Command that gets instantiated,
* // not MyCommand
* public static function main($exit = true)
* {
* $command = new static;
*
* return $command->run($_SERVER['argv'], $exit);
* }
*
* }
* </code>
*
* @throws Exception
*/
protected function handleArguments(array $argv): void
{
try {
$this->options = Getopt::getopt(
$argv,
'd:c:hv',
\array_keys($this->longOptions)
);
} catch (Exception $t) {
$this->exitWithErrorMessage($t->getMessage());
}
foreach ($this->options[0] as $option) {
switch ($option[0]) {
case '--colors':
$this->arguments['colors'] = $option[1] ?: ResultPrinter::COLOR_AUTO;
break;
case '--bootstrap':
$this->arguments['bootstrap'] = $option[1];
break;
case '--cache-result':
$this->arguments['cacheResult'] = true;
break;
case '--do-not-cache-result':
$this->arguments['cacheResult'] = false;
break;
case '--cache-result-file':
$this->arguments['cacheResultFile'] = $option[1];
break;
case '--columns':
if (\is_numeric($option[1])) {
$this->arguments['columns'] = (int) $option[1];
} elseif ($option[1] === 'max') {
$this->arguments['columns'] = 'max';
}
break;
case 'c':
case '--configuration':
$this->arguments['configuration'] = $option[1];
break;
case '--coverage-clover':
$this->arguments['coverageClover'] = $option[1];
break;
case '--coverage-crap4j':
$this->arguments['coverageCrap4J'] = $option[1];
break;
case '--coverage-html':
$this->arguments['coverageHtml'] = $option[1];
break;
case '--coverage-php':
$this->arguments['coveragePHP'] = $option[1];
break;
case '--coverage-text':
if ($option[1] === null) {
$option[1] = 'php://stdout';
}
$this->arguments['coverageText'] = $option[1];
$this->arguments['coverageTextShowUncoveredFiles'] = false;
$this->arguments['coverageTextShowOnlySummary'] = false;
break;
case '--coverage-xml':
$this->arguments['coverageXml'] = $option[1];
break;
case 'd':
$ini = \explode('=', $option[1]);
if (isset($ini[0])) {
if (isset($ini[1])) {
\ini_set($ini[0], $ini[1]);
} else {
\ini_set($ini[0], '1');
}
}
break;
case '--debug':
$this->arguments['debug'] = true;
break;
case 'h':
case '--help':
$this->showHelp();
exit(TestRunner::SUCCESS_EXIT);
break;
case '--filter':
$this->arguments['filter'] = $option[1];
break;
case '--testsuite':
$this->arguments['testsuite'] = $option[1];
break;
case '--generate-configuration':
$this->printVersionString();
print 'Generating phpunit.xml in ' . \getcwd() . \PHP_EOL . \PHP_EOL;
print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): ';
$bootstrapScript = \trim(\fgets(\STDIN));
print 'Tests directory (relative to path shown above; default: tests): ';
$testsDirectory = \trim(\fgets(\STDIN));
print 'Source directory (relative to path shown above; default: src): ';
$src = \trim(\fgets(\STDIN));
if ($bootstrapScript === '') {
$bootstrapScript = 'vendor/autoload.php';
}
if ($testsDirectory === '') {
$testsDirectory = 'tests';
}
if ($src === '') {
$src = 'src';
}
$generator = new ConfigurationGenerator;
\file_put_contents(
'phpunit.xml',
$generator->generateDefaultConfiguration(
Version::series(),
$bootstrapScript,
$testsDirectory,
$src
)
);
print \PHP_EOL . 'Generated phpunit.xml in ' . \getcwd() . \PHP_EOL;
exit(TestRunner::SUCCESS_EXIT);
break;
case '--group':
$this->arguments['groups'] = \explode(',', $option[1]);
break;
case '--exclude-group':
$this->arguments['excludeGroups'] = \explode(
',',
$option[1]
);
break;
case '--test-suffix':
$this->arguments['testSuffixes'] = \explode(
',',
$option[1]
);
break;
case '--include-path':
$includePath = $option[1];
break;
case '--list-groups':
$this->arguments['listGroups'] = true;
break;
case '--list-suites':
$this->arguments['listSuites'] = true;
break;
case '--list-tests':
$this->arguments['listTests'] = true;
break;
case '--list-tests-xml':
$this->arguments['listTestsXml'] = $option[1];
break;
case '--printer':
$this->arguments['printer'] = $option[1];
break;
case '--loader':
$this->arguments['loader'] = $option[1];
break;
case '--log-junit':
$this->arguments['junitLogfile'] = $option[1];
break;
case '--log-teamcity':
$this->arguments['teamcityLogfile'] = $option[1];
break;
case '--order-by':
$this->handleOrderByOption($option[1]);
break;
case '--process-isolation':
$this->arguments['processIsolation'] = true;
break;
case '--repeat':
$this->arguments['repeat'] = (int) $option[1];
break;
case '--stderr':
$this->arguments['stderr'] = true;
break;
case '--stop-on-defect':
$this->arguments['stopOnDefect'] = true;
break;
case '--stop-on-error':
$this->arguments['stopOnError'] = true;
break;
case '--stop-on-failure':
$this->arguments['stopOnFailure'] = true;
break;
case '--stop-on-warning':
$this->arguments['stopOnWarning'] = true;
break;
case '--stop-on-incomplete':
$this->arguments['stopOnIncomplete'] = true;
break;
case '--stop-on-risky':
$this->arguments['stopOnRisky'] = true;
break;
case '--stop-on-skipped':
$this->arguments['stopOnSkipped'] = true;
break;
case '--fail-on-warning':
$this->arguments['failOnWarning'] = true;
break;
case '--fail-on-risky':
$this->arguments['failOnRisky'] = true;
break;
case '--teamcity':
$this->arguments['printer'] = TeamCity::class;
break;
case '--testdox':
$this->arguments['printer'] = CliTestDoxPrinter::class;
break;
case '--testdox-group':
$this->arguments['testdoxGroups'] = \explode(
',',
$option[1]
);
break;
case '--testdox-exclude-group':
$this->arguments['testdoxExcludeGroups'] = \explode(
',',
$option[1]
);
break;
case '--testdox-html':
$this->arguments['testdoxHTMLFile'] = $option[1];
break;
case '--testdox-text':
$this->arguments['testdoxTextFile'] = $option[1];
break;
case '--testdox-xml':
$this->arguments['testdoxXMLFile'] = $option[1];
break;
case '--no-configuration':
$this->arguments['useDefaultConfiguration'] = false;
break;
case '--no-extensions':
$this->arguments['noExtensions'] = true;
break;
case '--no-coverage':
$this->arguments['noCoverage'] = true;
break;
case '--no-logging':
$this->arguments['noLogging'] = true;
break;
case '--no-interaction':
$this->arguments['noInteraction'] = true;
break;
case '--globals-backup':
$this->arguments['backupGlobals'] = true;
break;
case '--static-backup':
$this->arguments['backupStaticAttributes'] = true;
break;
case 'v':
case '--verbose':
$this->arguments['verbose'] = true;
break;
case '--atleast-version':
if (\version_compare(Version::id(), $option[1], '>=')) {
exit(TestRunner::SUCCESS_EXIT);
}
exit(TestRunner::FAILURE_EXIT);
break;
case '--version':
$this->printVersionString();
exit(TestRunner::SUCCESS_EXIT);
break;
case '--dont-report-useless-tests':
$this->arguments['reportUselessTests'] = false;
break;
case '--strict-coverage':
$this->arguments['strictCoverage'] = true;
break;
case '--disable-coverage-ignore':
$this->arguments['disableCodeCoverageIgnore'] = true;
break;
case '--strict-global-state':
$this->arguments['beStrictAboutChangesToGlobalState'] = true;
break;
case '--disallow-test-output':
$this->arguments['disallowTestOutput'] = true;
break;
case '--disallow-resource-usage':
$this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true;
break;
case '--default-time-limit':
$this->arguments['defaultTimeLimit'] = (int) $option[1];
break;
case '--enforce-time-limit':
$this->arguments['enforceTimeLimit'] = true;
break;
case '--disallow-todo-tests':
$this->arguments['disallowTodoAnnotatedTests'] = true;
break;
case '--reverse-list':
$this->arguments['reverseList'] = true;
break;
case '--check-version':
$this->handleVersionCheck();
break;
case '--whitelist':
$this->arguments['whitelist'] = $option[1];
break;
case '--random-order':
$this->handleOrderByOption('random');
break;
case '--random-order-seed':
$this->arguments['randomOrderSeed'] = (int) $option[1];
break;
case '--resolve-dependencies':
$this->handleOrderByOption('depends');
break;
case '--ignore-dependencies':
$this->handleOrderByOption('no-depends');
break;
case '--reverse-order':
$this->handleOrderByOption('reverse');
break;
case '--dump-xdebug-filter':
$this->arguments['xdebugFilterFile'] = $option[1];
break;
default:
$optionName = \str_replace('--', '', $option[0]);
$handler = null;
if (isset($this->longOptions[$optionName])) {
$handler = $this->longOptions[$optionName];
} elseif (isset($this->longOptions[$optionName . '='])) {
$handler = $this->longOptions[$optionName . '='];
}
if (isset($handler) && \is_callable([$this, $handler])) {
$this->$handler($option[1]);
}
}
}
$this->handleCustomTestSuite();
if (!isset($this->arguments['testSuffixes'])) {
$this->arguments['testSuffixes'] = ['Test.php', '.phpt'];
}
if (!isset($this->arguments['test'])) {
if (isset($this->options[1][0])) {
$this->arguments['test'] = $this->options[1][0];
}
if (isset($this->options[1][1])) {
$testFile = \realpath($this->options[1][1]);
if ($testFile === false) {
$this->exitWithErrorMessage(
\sprintf(
'Cannot open file "%s".',
$this->options[1][1]
)
);
}
$this->arguments['testFile'] = $testFile;
} else {
$this->arguments['testFile'] = '';
}
if (isset($this->arguments['test']) &&
\is_file($this->arguments['test']) &&
\strrpos($this->arguments['test'], '.') !== false &&
\substr($this->arguments['test'], -5, 5) !== '.phpt') {
$this->arguments['testFile'] = \realpath($this->arguments['test']);
$this->arguments['test'] = \substr($this->arguments['test'], 0, \strrpos($this->arguments['test'], '.'));
}
if (isset($this->arguments['test']) &&
\is_string($this->arguments['test']) &&
\substr($this->arguments['test'], -5, 5) === '.phpt') {
$suite = new TestSuite;
$suite->addTestFile($this->arguments['test']);
$this->arguments['test'] = $suite;
}
}
if (isset($includePath)) {
\ini_set(
'include_path',
$includePath . \PATH_SEPARATOR . \ini_get('include_path')
);
}
if ($this->arguments['loader'] !== null) {
$this->arguments['loader'] = $this->handleLoader($this->arguments['loader']);
}
if (isset($this->arguments['configuration']) &&
\is_dir($this->arguments['configuration'])) {
$configurationFile = $this->arguments['configuration'] . '/phpunit.xml';
if (\file_exists($configurationFile)) {
$this->arguments['configuration'] = \realpath(
$configurationFile
);
} elseif (\file_exists($configurationFile . '.dist')) {
$this->arguments['configuration'] = \realpath(
$configurationFile . '.dist'
);
}
} elseif (!isset($this->arguments['configuration']) &&
$this->arguments['useDefaultConfiguration']) {
if (\file_exists('phpunit.xml')) {
$this->arguments['configuration'] = \realpath('phpunit.xml');
} elseif (\file_exists('phpunit.xml.dist')) {
$this->arguments['configuration'] = \realpath(
'phpunit.xml.dist'
);
}
}
if (isset($this->arguments['configuration'])) {
try {
$configuration = Configuration::getInstance(
$this->arguments['configuration']
);
} catch (Throwable $t) {
print $t->getMessage() . \PHP_EOL;
exit(TestRunner::FAILURE_EXIT);
}
$phpunitConfiguration = $configuration->getPHPUnitConfiguration();
$configuration->handlePHPConfiguration();
/*
* Issue #1216
*/
if (isset($this->arguments['bootstrap'])) {
$this->handleBootstrap($this->arguments['bootstrap']);
} elseif (isset($phpunitConfiguration['bootstrap'])) {
$this->handleBootstrap($phpunitConfiguration['bootstrap']);
}
/*
* Issue #657
*/
if (isset($phpunitConfiguration['stderr']) && !isset($this->arguments['stderr'])) {
$this->arguments['stderr'] = $phpunitConfiguration['stderr'];
}
if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && \extension_loaded('phar')) {
$this->handleExtensions($phpunitConfiguration['extensionsDirectory']);
}
if (isset($phpunitConfiguration['columns']) && !isset($this->arguments['columns'])) {
$this->arguments['columns'] = $phpunitConfiguration['columns'];
}
if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) {
$file = $phpunitConfiguration['printerFile'] ?? '';
$this->arguments['printer'] = $this->handlePrinter(
$phpunitConfiguration['printerClass'],
$file
);
}
if (isset($phpunitConfiguration['testSuiteLoaderClass'])) {
$file = $phpunitConfiguration['testSuiteLoaderFile'] ?? '';
$this->arguments['loader'] = $this->handleLoader(
$phpunitConfiguration['testSuiteLoaderClass'],
$file
);
}
if (!isset($this->arguments['testsuite']) && isset($phpunitConfiguration['defaultTestSuite'])) {
$this->arguments['testsuite'] = $phpunitConfiguration['defaultTestSuite'];
}
if (!isset($this->arguments['test'])) {
$testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? '');
if ($testSuite !== null) {
$this->arguments['test'] = $testSuite;
}
}
} elseif (isset($this->arguments['bootstrap'])) {
$this->handleBootstrap($this->arguments['bootstrap']);
}
if (isset($this->arguments['printer']) &&
\is_string($this->arguments['printer'])) {
$this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']);
}
if (!isset($this->arguments['test'])) {
$this->showHelp();
exit(TestRunner::EXCEPTION_EXIT);
}
}
/**
* Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation.
*/
protected function handleLoader(string $loaderClass, string $loaderFile = ''): ?TestSuiteLoader
{
if (!\class_exists($loaderClass, false)) {
if ($loaderFile == '') {
$loaderFile = Filesystem::classNameToFilename(
$loaderClass
);
}
$loaderFile = \stream_resolve_include_path($loaderFile);
if ($loaderFile) {
require $loaderFile;
}
}
if (\class_exists($loaderClass, false)) {
try {
$class = new ReflectionClass($loaderClass);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($class->implementsInterface(TestSuiteLoader::class) && $class->isInstantiable()) {
$object = $class->newInstance();
\assert($object instanceof TestSuiteLoader);
return $object;
}
}
if ($loaderClass == StandardTestSuiteLoader::class) {
return null;
}
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as loader.',
$loaderClass
)
);
return null;
}
/**
* Handles the loading of the PHPUnit\Util\Printer implementation.
*
* @return null|Printer|string
*/
protected function handlePrinter(string $printerClass, string $printerFile = '')
{
if (!\class_exists($printerClass, false)) {
if ($printerFile == '') {
$printerFile = Filesystem::classNameToFilename(
$printerClass
);
}
$printerFile = \stream_resolve_include_path($printerFile);
if ($printerFile) {
require $printerFile;
}
}
if (!\class_exists($printerClass)) {
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as printer: class does not exist',
$printerClass
)
);
}
try {
$class = new ReflectionClass($printerClass);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$class->implementsInterface(TestListener::class)) {
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as printer: class does not implement %s',
$printerClass,
TestListener::class
)
);
}
if (!$class->isSubclassOf(Printer::class)) {
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as printer: class does not extend %s',
$printerClass,
Printer::class
)
);
}
if (!$class->isInstantiable()) {
$this->exitWithErrorMessage(
\sprintf(
'Could not use "%s" as printer: class cannot be instantiated',
$printerClass
)
);
}
if ($class->isSubclassOf(ResultPrinter::class)) {
return $printerClass;
}
$outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null;
return $class->newInstance($outputStream);
}
/**
* Loads a bootstrap file.
*/
protected function handleBootstrap(string $filename): void
{
try {
FileLoader::checkAndLoad($filename);
} catch (Exception $e) {
$this->exitWithErrorMessage($e->getMessage());
}
}
protected function handleVersionCheck(): void
{
$this->printVersionString();
$latestVersion = \file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit');
$isOutdated = \version_compare($latestVersion, Version::id(), '>');
if ($isOutdated) {
\printf(
'You are not using the latest version of PHPUnit.' . \PHP_EOL .
'The latest version is PHPUnit %s.' . \PHP_EOL,
$latestVersion
);
} else {
print 'You are using the latest version of PHPUnit.' . \PHP_EOL;
}
exit(TestRunner::SUCCESS_EXIT);
}
/**
* Show the help message.
*/
protected function showHelp(): void
{
$this->printVersionString();
(new Help)->writeToConsole();
}
/**
* Custom callback for test suite discovery.
*/
protected function handleCustomTestSuite(): void
{
}
private function printVersionString(): void
{
if ($this->versionStringPrinted) {
return;
}
print Version::getVersionString() . \PHP_EOL . \PHP_EOL;
$this->versionStringPrinted = true;
}
private function exitWithErrorMessage(string $message): void
{
$this->printVersionString();
print $message . \PHP_EOL;
exit(TestRunner::FAILURE_EXIT);
}
private function handleExtensions(string $directory): void
{
foreach ((new FileIteratorFacade)->getFilesAsArray($directory, '.phar') as $file) {
if (!\file_exists('phar://' . $file . '/manifest.xml')) {
$this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit';
continue;
}
try {
$applicationName = new ApplicationName('phpunit/phpunit');
$version = new PharIoVersion(Version::series());
$manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml');
if (!$manifest->isExtensionFor($applicationName)) {
$this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit';
continue;
}
if (!$manifest->isExtensionFor($applicationName, $version)) {
$this->arguments['notLoadedExtensions'][] = $file . ' is not compatible with this version of PHPUnit';
continue;
}
} catch (ManifestException $e) {
$this->arguments['notLoadedExtensions'][] = $file . ': ' . $e->getMessage();
continue;
}
require $file;
$this->arguments['loadedExtensions'][] = $manifest->getName() . ' ' . $manifest->getVersion()->getVersionString();
}
}
private function handleListGroups(TestSuite $suite, bool $exit): int
{
$this->printVersionString();
print 'Available test group(s):' . \PHP_EOL;
$groups = $suite->getGroups();
\sort($groups);
foreach ($groups as $group) {
\printf(
' - %s' . \PHP_EOL,
$group
);
}
if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}
return TestRunner::SUCCESS_EXIT;
}
/**
* @throws \PHPUnit\Framework\Exception
*/
private function handleListSuites(bool $exit): int
{
$this->printVersionString();
print 'Available test suite(s):' . \PHP_EOL;
$configuration = Configuration::getInstance(
$this->arguments['configuration']
);
foreach ($configuration->getTestSuiteNames() as $suiteName) {
\printf(
' - %s' . \PHP_EOL,
$suiteName
);
}
if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}
return TestRunner::SUCCESS_EXIT;
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function handleListTests(TestSuite $suite, bool $exit): int
{
$this->printVersionString();
$renderer = new TextTestListRenderer;
print $renderer->render($suite);
if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}
return TestRunner::SUCCESS_EXIT;
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function handleListTestsXml(TestSuite $suite, string $target, bool $exit): int
{
$this->printVersionString();
$renderer = new XmlTestListRenderer;
\file_put_contents($target, $renderer->render($suite));
\printf(
'Wrote list of tests that would have been run to %s' . \PHP_EOL,
$target
);
if ($exit) {
exit(TestRunner::SUCCESS_EXIT);
}
return TestRunner::SUCCESS_EXIT;
}
private function handleOrderByOption(string $value): void
{
foreach (\explode(',', $value) as $order) {
switch ($order) {
case 'default':
$this->arguments['executionOrder'] = TestSuiteSorter::ORDER_DEFAULT;
$this->arguments['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFAULT;
$this->arguments['resolveDependencies'] = true;
break;
case 'defects':
$this->arguments['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFECTS_FIRST;
break;
case 'depends':
$this->arguments['resolveDependencies'] = true;
break;
case 'duration':
$this->arguments['executionOrder'] = TestSuiteSorter::ORDER_DURATION;
break;
case 'no-depends':
$this->arguments['resolveDependencies'] = false;
break;
case 'random':
$this->arguments['executionOrder'] = TestSuiteSorter::ORDER_RANDOMIZED;
break;
case 'reverse':
$this->arguments['executionOrder'] = TestSuiteSorter::ORDER_REVERSED;
break;
case 'size':
$this->arguments['executionOrder'] = TestSuiteSorter::ORDER_SIZE;
break;
default:
$this->exitWithErrorMessage("unrecognized --order-by option: $order");
}
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Exception extends \RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\InvalidArgumentException;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\Color;
use PHPUnit\Util\Printer;
use SebastianBergmann\Environment\Console;
use SebastianBergmann\Timer\Timer;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class ResultPrinter extends Printer implements TestListener
{
public const EVENT_TEST_START = 0;
public const EVENT_TEST_END = 1;
public const EVENT_TESTSUITE_START = 2;
public const EVENT_TESTSUITE_END = 3;
public const COLOR_NEVER = 'never';
public const COLOR_AUTO = 'auto';
public const COLOR_ALWAYS = 'always';
public const COLOR_DEFAULT = self::COLOR_NEVER;
private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS];
/**
* @var int
*/
protected $column = 0;
/**
* @var int
*/
protected $maxColumn;
/**
* @var bool
*/
protected $lastTestFailed = false;
/**
* @var int
*/
protected $numAssertions = 0;
/**
* @var int
*/
protected $numTests = -1;
/**
* @var int
*/
protected $numTestsRun = 0;
/**
* @var int
*/
protected $numTestsWidth;
/**
* @var bool
*/
protected $colors = false;
/**
* @var bool
*/
protected $debug = false;
/**
* @var bool
*/
protected $verbose = false;
/**
* @var int
*/
private $numberOfColumns;
/**
* @var bool
*/
private $reverse;
/**
* @var bool
*/
private $defectListPrinted = false;
/**
* Constructor.
*
* @param null|resource|string $out
* @param int|string $numberOfColumns
*
* @throws Exception
*/
public function __construct($out = null, bool $verbose = false, string $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false)
{
parent::__construct($out);
if (!\in_array($colors, self::AVAILABLE_COLORS, true)) {
throw InvalidArgumentException::create(
3,
\vsprintf('value from "%s", "%s" or "%s"', self::AVAILABLE_COLORS)
);
}
if (!\is_int($numberOfColumns) && $numberOfColumns !== 'max') {
throw InvalidArgumentException::create(5, 'integer or "max"');
}
$console = new Console;
$maxNumberOfColumns = $console->getNumberOfColumns();
if ($numberOfColumns === 'max' || ($numberOfColumns !== 80 && $numberOfColumns > $maxNumberOfColumns)) {
$numberOfColumns = $maxNumberOfColumns;
}
$this->numberOfColumns = $numberOfColumns;
$this->verbose = $verbose;
$this->debug = $debug;
$this->reverse = $reverse;
if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) {
$this->colors = true;
} else {
$this->colors = (self::COLOR_ALWAYS === $colors);
}
}
/**
* @throws \SebastianBergmann\Timer\RuntimeException
*/
public function printResult(TestResult $result): void
{
$this->printHeader();
$this->printErrors($result);
$this->printWarnings($result);
$this->printFailures($result);
$this->printRisky($result);
if ($this->verbose) {
$this->printIncompletes($result);
$this->printSkipped($result);
}
$this->printFooter($result);
}
/**
* An error occurred.
*/
public function addError(Test $test, \Throwable $t, float $time): void
{
$this->writeProgressWithColor('fg-red, bold', 'E');
$this->lastTestFailed = true;
}
/**
* A failure occurred.
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->writeProgressWithColor('bg-red, fg-white', 'F');
$this->lastTestFailed = true;
}
/**
* A warning occurred.
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
$this->writeProgressWithColor('fg-yellow, bold', 'W');
$this->lastTestFailed = true;
}
/**
* Incomplete test.
*/
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
{
$this->writeProgressWithColor('fg-yellow, bold', 'I');
$this->lastTestFailed = true;
}
/**
* Risky test.
*/
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
{
$this->writeProgressWithColor('fg-yellow, bold', 'R');
$this->lastTestFailed = true;
}
/**
* Skipped test.
*/
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
{
$this->writeProgressWithColor('fg-cyan, bold', 'S');
$this->lastTestFailed = true;
}
/**
* A testsuite started.
*/
public function startTestSuite(TestSuite $suite): void
{
if ($this->numTests == -1) {
$this->numTests = \count($suite);
$this->numTestsWidth = \strlen((string) $this->numTests);
$this->maxColumn = $this->numberOfColumns - \strlen(' / (XXX%)') - (2 * $this->numTestsWidth);
}
}
/**
* A testsuite ended.
*/
public function endTestSuite(TestSuite $suite): void
{
}
/**
* A test started.
*/
public function startTest(Test $test): void
{
if ($this->debug) {
$this->write(
\sprintf(
"Test '%s' started\n",
\PHPUnit\Util\Test::describeAsString($test)
)
);
}
}
/**
* A test ended.
*/
public function endTest(Test $test, float $time): void
{
if ($this->debug) {
$this->write(
\sprintf(
"Test '%s' ended\n",
\PHPUnit\Util\Test::describeAsString($test)
)
);
}
if (!$this->lastTestFailed) {
$this->writeProgress('.');
}
if ($test instanceof TestCase) {
$this->numAssertions += $test->getNumAssertions();
} elseif ($test instanceof PhptTestCase) {
$this->numAssertions++;
}
$this->lastTestFailed = false;
if ($test instanceof TestCase && !$test->hasExpectationOnOutput()) {
$this->write($test->getActualOutput());
}
}
protected function printDefects(array $defects, string $type): void
{
$count = \count($defects);
if ($count == 0) {
return;
}
if ($this->defectListPrinted) {
$this->write("\n--\n\n");
}
$this->write(
\sprintf(
"There %s %d %s%s:\n",
($count == 1) ? 'was' : 'were',
$count,
$type,
($count == 1) ? '' : 's'
)
);
$i = 1;
if ($this->reverse) {
$defects = \array_reverse($defects);
}
foreach ($defects as $defect) {
$this->printDefect($defect, $i++);
}
$this->defectListPrinted = true;
}
protected function printDefect(TestFailure $defect, int $count): void
{
$this->printDefectHeader($defect, $count);
$this->printDefectTrace($defect);
}
protected function printDefectHeader(TestFailure $defect, int $count): void
{
$this->write(
\sprintf(
"\n%d) %s\n",
$count,
$defect->getTestName()
)
);
}
protected function printDefectTrace(TestFailure $defect): void
{
$e = $defect->thrownException();
$this->write((string) $e);
while ($e = $e->getPrevious()) {
$this->write("\nCaused by\n" . $e);
}
}
protected function printErrors(TestResult $result): void
{
$this->printDefects($result->errors(), 'error');
}
protected function printFailures(TestResult $result): void
{
$this->printDefects($result->failures(), 'failure');
}
protected function printWarnings(TestResult $result): void
{
$this->printDefects($result->warnings(), 'warning');
}
protected function printIncompletes(TestResult $result): void
{
$this->printDefects($result->notImplemented(), 'incomplete test');
}
protected function printRisky(TestResult $result): void
{
$this->printDefects($result->risky(), 'risky test');
}
protected function printSkipped(TestResult $result): void
{
$this->printDefects($result->skipped(), 'skipped test');
}
/**
* @throws \SebastianBergmann\Timer\RuntimeException
*/
protected function printHeader(): void
{
$this->write("\n\n" . Timer::resourceUsage() . "\n\n");
}
protected function printFooter(TestResult $result): void
{
if (\count($result) === 0) {
$this->writeWithColor(
'fg-black, bg-yellow',
'No tests executed!'
);
return;
}
if ($result->wasSuccessful() &&
$result->allHarmless() &&
$result->allCompletelyImplemented() &&
$result->noneSkipped()) {
$this->writeWithColor(
'fg-black, bg-green',
\sprintf(
'OK (%d test%s, %d assertion%s)',
\count($result),
(\count($result) == 1) ? '' : 's',
$this->numAssertions,
($this->numAssertions == 1) ? '' : 's'
)
);
} else {
if ($result->wasSuccessful()) {
$color = 'fg-black, bg-yellow';
if ($this->verbose || !$result->allHarmless()) {
$this->write("\n");
}
$this->writeWithColor(
$color,
'OK, but incomplete, skipped, or risky tests!'
);
} else {
$this->write("\n");
if ($result->errorCount()) {
$color = 'fg-white, bg-red';
$this->writeWithColor(
$color,
'ERRORS!'
);
} elseif ($result->failureCount()) {
$color = 'fg-white, bg-red';
$this->writeWithColor(
$color,
'FAILURES!'
);
} elseif ($result->warningCount()) {
$color = 'fg-black, bg-yellow';
$this->writeWithColor(
$color,
'WARNINGS!'
);
}
}
$this->writeCountString(\count($result), 'Tests', $color, true);
$this->writeCountString($this->numAssertions, 'Assertions', $color, true);
$this->writeCountString($result->errorCount(), 'Errors', $color);
$this->writeCountString($result->failureCount(), 'Failures', $color);
$this->writeCountString($result->warningCount(), 'Warnings', $color);
$this->writeCountString($result->skippedCount(), 'Skipped', $color);
$this->writeCountString($result->notImplementedCount(), 'Incomplete', $color);
$this->writeCountString($result->riskyCount(), 'Risky', $color);
$this->writeWithColor($color, '.');
}
}
protected function writeProgress(string $progress): void
{
if ($this->debug) {
return;
}
$this->write($progress);
$this->column++;
$this->numTestsRun++;
if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests) {
if ($this->numTestsRun == $this->numTests) {
$this->write(\str_repeat(' ', $this->maxColumn - $this->column));
}
$this->write(
\sprintf(
' %' . $this->numTestsWidth . 'd / %' .
$this->numTestsWidth . 'd (%3s%%)',
$this->numTestsRun,
$this->numTests,
\floor(($this->numTestsRun / $this->numTests) * 100)
)
);
if ($this->column == $this->maxColumn) {
$this->writeNewLine();
}
}
}
protected function writeNewLine(): void
{
$this->column = 0;
$this->write("\n");
}
/**
* Formats a buffer with a specified ANSI color sequence if colors are
* enabled.
*/
protected function colorizeTextBox(string $color, string $buffer): string
{
if (!$this->colors) {
return $buffer;
}
$lines = \preg_split('/\r\n|\r|\n/', $buffer);
$padding = \max(\array_map('\strlen', $lines));
$styledLines = [];
foreach ($lines as $line) {
$styledLines[] = Color::colorize($color, \str_pad($line, $padding));
}
return \implode(\PHP_EOL, $styledLines);
}
/**
* Writes a buffer out with a color sequence if colors are enabled.
*/
protected function writeWithColor(string $color, string $buffer, bool $lf = true): void
{
$this->write($this->colorizeTextBox($color, $buffer));
if ($lf) {
$this->write(\PHP_EOL);
}
}
/**
* Writes progress with a color sequence if colors are enabled.
*/
protected function writeProgressWithColor(string $color, string $buffer): void
{
$buffer = $this->colorizeTextBox($color, $buffer);
$this->writeProgress($buffer);
}
private function writeCountString(int $count, string $name, string $color, bool $always = false): void
{
static $first = true;
if ($always || $count > 0) {
$this->writeWithColor(
$color,
\sprintf(
'%s%s: %d',
!$first ? ', ' : '',
$name,
$count
),
false
);
$first = false;
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface Exception extends \Throwable
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class RegularExpression
{
/**
* @return false|int
*/
public static function safeMatch(string $pattern, string $subject, ?array $matches = null, int $flags = 0, int $offset = 0)
{
return ErrorHandler::invokeIgnoringWarnings(
static function () use ($pattern, $subject, $matches, $flags, $offset) {
return \preg_match($pattern, $subject, $matches, $flags, $offset);
}
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\CodeCoverageException;
use PHPUnit\Framework\InvalidCoversTargetException;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\Version;
use PHPUnit\Util\Annotation\Registry;
use SebastianBergmann\Environment\OperatingSystem;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Test
{
/**
* @var int
*/
public const UNKNOWN = -1;
/**
* @var int
*/
public const SMALL = 0;
/**
* @var int
*/
public const MEDIUM = 1;
/**
* @var int
*/
public const LARGE = 2;
/**
* @var array
*/
private static $hookMethods = [];
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function describe(\PHPUnit\Framework\Test $test): array
{
if ($test instanceof TestCase) {
return [\get_class($test), $test->getName()];
}
if ($test instanceof SelfDescribing) {
return ['', $test->toString()];
}
return ['', \get_class($test)];
}
public static function describeAsString(\PHPUnit\Framework\Test $test): string
{
if ($test instanceof SelfDescribing) {
return $test->toString();
}
return \get_class($test);
}
/**
* @throws CodeCoverageException
*
* @return array|bool
* @psalm-param class-string $className
*/
public static function getLinesToBeCovered(string $className, string $methodName)
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
if (!self::shouldCoversAnnotationBeUsed($annotations)) {
return false;
}
return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers');
}
/**
* Returns lines of code specified with the @uses annotation.
*
* @throws CodeCoverageException
* @psalm-param class-string $className
*/
public static function getLinesToBeUsed(string $className, string $methodName): array
{
return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses');
}
public static function requiresCodeCoverageDataCollection(TestCase $test): bool
{
$annotations = $test->getAnnotations();
// If there is no @covers annotation but a @coversNothing annotation on
// the test method then code coverage data does not need to be collected
if (isset($annotations['method']['coversNothing'])) {
return false;
}
// If there is at least one @covers annotation then
// code coverage data needs to be collected
if (isset($annotations['method']['covers'])) {
return true;
}
// If there is no @covers annotation but a @coversNothing annotation
// then code coverage data does not need to be collected
if (isset($annotations['class']['coversNothing'])) {
return false;
}
// If there is no @coversNothing annotation then
// code coverage data may be collected
return true;
}
/**
* @throws Exception
* @psalm-param class-string $className
*/
public static function getRequirements(string $className, string $methodName): array
{
return self::mergeArraysRecursively(
Registry::getInstance()->forClassName($className)->requirements(),
Registry::getInstance()->forMethod($className, $methodName)->requirements()
);
}
/**
* Returns the missing requirements for a test.
*
* @throws Exception
* @throws Warning
* @psalm-param class-string $className
*/
public static function getMissingRequirements(string $className, string $methodName): array
{
$required = static::getRequirements($className, $methodName);
$missing = [];
$hint = null;
if (!empty($required['PHP'])) {
$operator = empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator'];
self::ensureOperatorIsValid($operator);
if (!\version_compare(\PHP_VERSION, $required['PHP']['version'], $operator)) {
$missing[] = \sprintf('PHP %s %s is required.', $operator, $required['PHP']['version']);
$hint = $hint ?? 'PHP';
}
} elseif (!empty($required['PHP_constraint'])) {
$version = new \PharIo\Version\Version(self::sanitizeVersionNumber(\PHP_VERSION));
if (!$required['PHP_constraint']['constraint']->complies($version)) {
$missing[] = \sprintf(
'PHP version does not match the required constraint %s.',
$required['PHP_constraint']['constraint']->asString()
);
$hint = $hint ?? 'PHP_constraint';
}
}
if (!empty($required['PHPUnit'])) {
$phpunitVersion = Version::id();
$operator = empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator'];
self::ensureOperatorIsValid($operator);
if (!\version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator)) {
$missing[] = \sprintf('PHPUnit %s %s is required.', $operator, $required['PHPUnit']['version']);
$hint = $hint ?? 'PHPUnit';
}
} elseif (!empty($required['PHPUnit_constraint'])) {
$phpunitVersion = new \PharIo\Version\Version(self::sanitizeVersionNumber(Version::id()));
if (!$required['PHPUnit_constraint']['constraint']->complies($phpunitVersion)) {
$missing[] = \sprintf(
'PHPUnit version does not match the required constraint %s.',
$required['PHPUnit_constraint']['constraint']->asString()
);
$hint = $hint ?? 'PHPUnit_constraint';
}
}
if (!empty($required['OSFAMILY']) && $required['OSFAMILY'] !== (new OperatingSystem)->getFamily()) {
$missing[] = \sprintf('Operating system %s is required.', $required['OSFAMILY']);
$hint = $hint ?? 'OSFAMILY';
}
if (!empty($required['OS'])) {
$requiredOsPattern = \sprintf('/%s/i', \addcslashes($required['OS'], '/'));
if (!\preg_match($requiredOsPattern, \PHP_OS)) {
$missing[] = \sprintf('Operating system matching %s is required.', $requiredOsPattern);
$hint = $hint ?? 'OS';
}
}
if (!empty($required['functions'])) {
foreach ($required['functions'] as $function) {
$pieces = \explode('::', $function);
if (\count($pieces) === 2 && \class_exists($pieces[0]) && \method_exists($pieces[0], $pieces[1])) {
continue;
}
if (\function_exists($function)) {
continue;
}
$missing[] = \sprintf('Function %s is required.', $function);
$hint = $hint ?? 'function_' . $function;
}
}
if (!empty($required['setting'])) {
foreach ($required['setting'] as $setting => $value) {
if (\ini_get($setting) !== $value) {
$missing[] = \sprintf('Setting "%s" must be "%s".', $setting, $value);
$hint = $hint ?? '__SETTING_' . $setting;
}
}
}
if (!empty($required['extensions'])) {
foreach ($required['extensions'] as $extension) {
if (isset($required['extension_versions'][$extension])) {
continue;
}
if (!\extension_loaded($extension)) {
$missing[] = \sprintf('Extension %s is required.', $extension);
$hint = $hint ?? 'extension_' . $extension;
}
}
}
if (!empty($required['extension_versions'])) {
foreach ($required['extension_versions'] as $extension => $req) {
$actualVersion = \phpversion($extension);
$operator = empty($req['operator']) ? '>=' : $req['operator'];
self::ensureOperatorIsValid($operator);
if ($actualVersion === false || !\version_compare($actualVersion, $req['version'], $operator)) {
$missing[] = \sprintf('Extension %s %s %s is required.', $extension, $operator, $req['version']);
$hint = $hint ?? 'extension_' . $extension;
}
}
}
if ($hint && isset($required['__OFFSET'])) {
\array_unshift($missing, '__OFFSET_FILE=' . $required['__OFFSET']['__FILE']);
\array_unshift($missing, '__OFFSET_LINE=' . ($required['__OFFSET'][$hint] ?? 1));
}
return $missing;
}
/**
* Returns the expected exception for a test.
*
* @return array|false
*
* @deprecated
* @codeCoverageIgnore
* @psalm-param class-string $className
*/
public static function getExpectedException(string $className, string $methodName)
{
return Registry::getInstance()->forMethod($className, $methodName)->expectedException();
}
/**
* Returns the provided data for a method.
*
* @throws Exception
* @psalm-param class-string $className
*/
public static function getProvidedData(string $className, string $methodName): ?array
{
return Registry::getInstance()->forMethod($className, $methodName)->getProvidedData();
}
/**
* @psalm-param class-string $className
*/
public static function parseTestMethodAnnotations(string $className, ?string $methodName = ''): array
{
$registry = Registry::getInstance();
if ($methodName !== null) {
try {
return [
'method' => $registry->forMethod($className, $methodName)->symbolAnnotations(),
'class' => $registry->forClassName($className)->symbolAnnotations(),
];
} catch (Exception $methodNotFound) {
// ignored
}
}
return [
'method' => null,
'class' => $registry->forClassName($className)->symbolAnnotations(),
];
}
/**
* @psalm-param class-string $className
*/
public static function getInlineAnnotations(string $className, string $methodName): array
{
return Registry::getInstance()->forMethod($className, $methodName)->getInlineAnnotations();
}
/** @psalm-param class-string $className */
public static function getBackupSettings(string $className, string $methodName): array
{
return [
'backupGlobals' => self::getBooleanAnnotationSetting(
$className,
$methodName,
'backupGlobals'
),
'backupStaticAttributes' => self::getBooleanAnnotationSetting(
$className,
$methodName,
'backupStaticAttributes'
),
];
}
/** @psalm-param class-string $className */
public static function getDependencies(string $className, string $methodName): array
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
$dependencies = $annotations['class']['depends'] ?? [];
if (isset($annotations['method']['depends'])) {
$dependencies = \array_merge(
$dependencies,
$annotations['method']['depends']
);
}
return \array_unique($dependencies);
}
/** @psalm-param class-string $className */
public static function getGroups(string $className, ?string $methodName = ''): array
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
$groups = [];
if (isset($annotations['method']['author'])) {
$groups[] = $annotations['method']['author'];
} elseif (isset($annotations['class']['author'])) {
$groups[] = $annotations['class']['author'];
}
if (isset($annotations['class']['group'])) {
$groups[] = $annotations['class']['group'];
}
if (isset($annotations['method']['group'])) {
$groups[] = $annotations['method']['group'];
}
if (isset($annotations['class']['ticket'])) {
$groups[] = $annotations['class']['ticket'];
}
if (isset($annotations['method']['ticket'])) {
$groups[] = $annotations['method']['ticket'];
}
foreach (['method', 'class'] as $element) {
foreach (['small', 'medium', 'large'] as $size) {
if (isset($annotations[$element][$size])) {
$groups[] = [$size];
break 2;
}
}
}
return \array_unique(\array_merge([], ...$groups));
}
/** @psalm-param class-string $className */
public static function getSize(string $className, ?string $methodName): int
{
$groups = \array_flip(self::getGroups($className, $methodName));
if (isset($groups['large'])) {
return self::LARGE;
}
if (isset($groups['medium'])) {
return self::MEDIUM;
}
if (isset($groups['small'])) {
return self::SMALL;
}
return self::UNKNOWN;
}
/** @psalm-param class-string $className */
public static function getProcessIsolationSettings(string $className, string $methodName): bool
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
return isset($annotations['class']['runTestsInSeparateProcesses']) || isset($annotations['method']['runInSeparateProcess']);
}
/** @psalm-param class-string $className */
public static function getClassProcessIsolationSettings(string $className, string $methodName): bool
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
return isset($annotations['class']['runClassInSeparateProcess']);
}
/** @psalm-param class-string $className */
public static function getPreserveGlobalStateSettings(string $className, string $methodName): ?bool
{
return self::getBooleanAnnotationSetting(
$className,
$methodName,
'preserveGlobalState'
);
}
/** @psalm-param class-string $className */
public static function getHookMethods(string $className): array
{
if (!\class_exists($className, false)) {
return self::emptyHookMethodsArray();
}
if (!isset(self::$hookMethods[$className])) {
self::$hookMethods[$className] = self::emptyHookMethodsArray();
try {
foreach ((new \ReflectionClass($className))->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() === Assert::class) {
continue;
}
if ($method->getDeclaringClass()->getName() === TestCase::class) {
continue;
}
$docBlock = Registry::getInstance()->forMethod($className, $method->getName());
if ($method->isStatic()) {
if ($docBlock->isHookToBeExecutedBeforeClass()) {
\array_unshift(
self::$hookMethods[$className]['beforeClass'],
$method->getName()
);
}
if ($docBlock->isHookToBeExecutedAfterClass()) {
self::$hookMethods[$className]['afterClass'][] = $method->getName();
}
}
if ($docBlock->isToBeExecutedBeforeTest()) {
\array_unshift(
self::$hookMethods[$className]['before'],
$method->getName()
);
}
if ($docBlock->isToBeExecutedAfterTest()) {
self::$hookMethods[$className]['after'][] = $method->getName();
}
}
} catch (\ReflectionException $e) {
}
}
return self::$hookMethods[$className];
}
public static function isTestMethod(\ReflectionMethod $method): bool
{
if (\strpos($method->getName(), 'test') === 0) {
return true;
}
return \array_key_exists(
'test',
Registry::getInstance()->forMethod(
$method->getDeclaringClass()->getName(),
$method->getName()
)
->symbolAnnotations()
);
}
/**
* @throws CodeCoverageException
* @psalm-param class-string $className
*/
private static function getLinesToBeCoveredOrUsed(string $className, string $methodName, string $mode): array
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
$classShortcut = null;
if (!empty($annotations['class'][$mode . 'DefaultClass'])) {
if (\count($annotations['class'][$mode . 'DefaultClass']) > 1) {
throw new CodeCoverageException(
\sprintf(
'More than one @%sClass annotation in class or interface "%s".',
$mode,
$className
)
);
}
$classShortcut = $annotations['class'][$mode . 'DefaultClass'][0];
}
$list = $annotations['class'][$mode] ?? [];
if (isset($annotations['method'][$mode])) {
$list = \array_merge($list, $annotations['method'][$mode]);
}
$codeList = [];
foreach (\array_unique($list) as $element) {
if ($classShortcut && \strncmp($element, '::', 2) === 0) {
$element = $classShortcut . $element;
}
$element = \preg_replace('/[\s()]+$/', '', $element);
$element = \explode(' ', $element);
$element = $element[0];
if ($mode === 'covers' && \interface_exists($element)) {
throw new InvalidCoversTargetException(
\sprintf(
'Trying to @cover interface "%s".',
$element
)
);
}
$codeList[] = self::resolveElementToReflectionObjects($element);
}
return self::resolveReflectionObjectsToLines(\array_merge([], ...$codeList));
}
private static function emptyHookMethodsArray(): array
{
return [
'beforeClass' => ['setUpBeforeClass'],
'before' => ['setUp'],
'after' => ['tearDown'],
'afterClass' => ['tearDownAfterClass'],
];
}
/** @psalm-param class-string $className */
private static function getBooleanAnnotationSetting(string $className, ?string $methodName, string $settingName): ?bool
{
$annotations = self::parseTestMethodAnnotations(
$className,
$methodName
);
if (isset($annotations['method'][$settingName])) {
if ($annotations['method'][$settingName][0] === 'enabled') {
return true;
}
if ($annotations['method'][$settingName][0] === 'disabled') {
return false;
}
}
if (isset($annotations['class'][$settingName])) {
if ($annotations['class'][$settingName][0] === 'enabled') {
return true;
}
if ($annotations['class'][$settingName][0] === 'disabled') {
return false;
}
}
return null;
}
/**
* @throws InvalidCoversTargetException
*/
private static function resolveElementToReflectionObjects(string $element): array
{
$codeToCoverList = [];
if (\function_exists($element) && \strpos($element, '\\') !== false) {
try {
$codeToCoverList[] = new \ReflectionFunction($element);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
} elseif (\strpos($element, '::') !== false) {
[$className, $methodName] = \explode('::', $element);
if (isset($methodName[0]) && $methodName[0] === '<') {
$classes = [$className];
foreach ($classes as $className) {
if (!\class_exists($className) &&
!\interface_exists($className) &&
!\trait_exists($className)) {
throw new InvalidCoversTargetException(
\sprintf(
'Trying to @cover or @use not existing class or ' .
'interface "%s".',
$className
)
);
}
try {
$methods = (new \ReflectionClass($className))->getMethods();
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$inverse = isset($methodName[1]) && $methodName[1] === '!';
$visibility = 'isPublic';
if (\strpos($methodName, 'protected')) {
$visibility = 'isProtected';
} elseif (\strpos($methodName, 'private')) {
$visibility = 'isPrivate';
}
foreach ($methods as $method) {
if ($inverse && !$method->$visibility()) {
$codeToCoverList[] = $method;
} elseif (!$inverse && $method->$visibility()) {
$codeToCoverList[] = $method;
}
}
}
} else {
$classes = [$className];
foreach ($classes as $className) {
if ($className === '' && \function_exists($methodName)) {
try {
$codeToCoverList[] = new \ReflectionFunction(
$methodName
);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
} else {
if (!((\class_exists($className) || \interface_exists($className) || \trait_exists($className)) &&
\method_exists($className, $methodName))) {
throw new InvalidCoversTargetException(
\sprintf(
'Trying to @cover or @use not existing method "%s::%s".',
$className,
$methodName
)
);
}
try {
$codeToCoverList[] = new \ReflectionMethod(
$className,
$methodName
);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
}
}
}
} else {
$extended = false;
if (\strpos($element, '<extended>') !== false) {
$element = \str_replace('<extended>', '', $element);
$extended = true;
}
$classes = [$element];
if ($extended) {
$classes = \array_merge(
$classes,
\class_implements($element),
\class_parents($element)
);
}
foreach ($classes as $className) {
if (!\class_exists($className) &&
!\interface_exists($className) &&
!\trait_exists($className)) {
throw new InvalidCoversTargetException(
\sprintf(
'Trying to @cover or @use not existing class or ' .
'interface "%s".',
$className
)
);
}
try {
$codeToCoverList[] = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
}
}
return $codeToCoverList;
}
private static function resolveReflectionObjectsToLines(array $reflectors): array
{
$result = [];
foreach ($reflectors as $reflector) {
if ($reflector instanceof \ReflectionClass) {
foreach ($reflector->getTraits() as $trait) {
$reflectors[] = $trait;
}
}
}
foreach ($reflectors as $reflector) {
$filename = $reflector->getFileName();
if (!isset($result[$filename])) {
$result[$filename] = [];
}
$result[$filename] = \array_merge(
$result[$filename],
\range($reflector->getStartLine(), $reflector->getEndLine())
);
}
foreach ($result as $filename => $lineNumbers) {
$result[$filename] = \array_keys(\array_flip($lineNumbers));
}
return $result;
}
/**
* Trims any extensions from version string that follows after
* the <major>.<minor>[.<patch>] format
*/
private static function sanitizeVersionNumber(string $version)
{
return \preg_replace(
'/^(\d+\.\d+(?:.\d+)?).*$/',
'$1',
$version
);
}
private static function shouldCoversAnnotationBeUsed(array $annotations): bool
{
if (isset($annotations['method']['coversNothing'])) {
return false;
}
if (isset($annotations['method']['covers'])) {
return true;
}
if (isset($annotations['class']['coversNothing'])) {
return false;
}
return true;
}
/**
* Merge two arrays together.
*
* If an integer key exists in both arrays and preserveNumericKeys is false, the value
* from the second array will be appended to the first array. If both values are arrays, they
* are merged together, else the value of the second array overwrites the one of the first array.
*
* This implementation is copied from https://github.com/zendframework/zend-stdlib/blob/76b653c5e99b40eccf5966e3122c90615134ae46/src/ArrayUtils.php
*
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
*
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
private static function mergeArraysRecursively(array $a, array $b): array
{
foreach ($b as $key => $value) {
if (\array_key_exists($key, $a)) {
if (\is_int($key)) {
$a[] = $value;
} elseif (\is_array($value) && \is_array($a[$key])) {
$a[$key] = self::mergeArraysRecursively($a[$key], $value);
} else {
$a[$key] = $value;
}
} else {
$a[$key] = $value;
}
}
return $a;
}
/*
* @throws Exception
*/
private static function ensureOperatorIsValid(string $operator): void
{
if (!\in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'])) {
throw new Exception(
\sprintf(
'"%s" is not a valid version_compare() operator',
$operator
)
);
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use Closure;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class GlobalState
{
/**
* @var string[]
*/
private const SUPER_GLOBAL_ARRAYS = [
'_ENV',
'_POST',
'_GET',
'_COOKIE',
'_SERVER',
'_FILES',
'_REQUEST',
];
/**
* @throws Exception
*/
public static function getIncludedFilesAsString(): string
{
return static::processIncludedFilesAsString(\get_included_files());
}
/**
* @param string[] $files
*
* @throws Exception
*/
public static function processIncludedFilesAsString(array $files): string
{
$blacklist = new Blacklist;
$prefix = false;
$result = '';
if (\defined('__PHPUNIT_PHAR__')) {
$prefix = 'phar://' . __PHPUNIT_PHAR__ . '/';
}
for ($i = \count($files) - 1; $i > 0; $i--) {
$file = $files[$i];
if (!empty($GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST']) &&
\in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'])) {
continue;
}
if ($prefix !== false && \strpos($file, $prefix) === 0) {
continue;
}
// Skip virtual file system protocols
if (\preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) {
continue;
}
if (!$blacklist->isBlacklisted($file) && \is_file($file)) {
$result = 'require_once \'' . $file . "';\n" . $result;
}
}
return $result;
}
public static function getIniSettingsAsString(): string
{
$result = '';
foreach (\ini_get_all(null, false) as $key => $value) {
$result .= \sprintf(
'@ini_set(%s, %s);' . "\n",
self::exportVariable($key),
self::exportVariable((string) $value)
);
}
return $result;
}
public static function getConstantsAsString(): string
{
$constants = \get_defined_constants(true);
$result = '';
if (isset($constants['user'])) {
foreach ($constants['user'] as $name => $value) {
$result .= \sprintf(
'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n",
$name,
$name,
self::exportVariable($value)
);
}
}
return $result;
}
public static function getGlobalsAsString(): string
{
$result = '';
foreach (self::SUPER_GLOBAL_ARRAYS as $superGlobalArray) {
if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) {
foreach (\array_keys($GLOBALS[$superGlobalArray]) as $key) {
if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) {
continue;
}
$result .= \sprintf(
'$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n",
$superGlobalArray,
$key,
self::exportVariable($GLOBALS[$superGlobalArray][$key])
);
}
}
}
$blacklist = self::SUPER_GLOBAL_ARRAYS;
$blacklist[] = 'GLOBALS';
foreach (\array_keys($GLOBALS) as $key) {
if (!$GLOBALS[$key] instanceof Closure && !\in_array($key, $blacklist, true)) {
$result .= \sprintf(
'$GLOBALS[\'%s\'] = %s;' . "\n",
$key,
self::exportVariable($GLOBALS[$key])
);
}
}
return $result;
}
private static function exportVariable($variable): string
{
if (\is_scalar($variable) || $variable === null ||
(\is_array($variable) && self::arrayOnlyContainsScalars($variable))) {
return \var_export($variable, true);
}
return 'unserialize(' . \var_export(\serialize($variable), true) . ')';
}
private static function arrayOnlyContainsScalars(array $array): bool
{
$result = true;
foreach ($array as $element) {
if (\is_array($element)) {
$result = self::arrayOnlyContainsScalars($element);
} elseif (!\is_scalar($element) && $element !== null) {
$result = false;
}
if (!$result) {
break;
}
}
return $result;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TextResultPrinter extends ResultPrinter
{
/**
* Handler for 'start class' event.
*/
protected function startClass(string $name): void
{
$this->write($this->currentTestClassPrettified . "\n");
}
/**
* Handler for 'on test' event.
*/
protected function onTest($name, bool $success = true): void
{
if ($success) {
$this->write(' [x] ');
} else {
$this->write(' [ ] ');
}
$this->write($name . "\n");
}
/**
* Handler for 'end class' event.
*/
protected function endClass(string $name): void
{
$this->write("\n");
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Runner\TestSuiteSorter;
use PHPUnit\TextUI\ResultPrinter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class TestDoxPrinter extends ResultPrinter
{
/**
* @var NamePrettifier
*/
protected $prettifier;
/**
* @var int The number of test results received from the TestRunner
*/
protected $testIndex = 0;
/**
* @var int The number of test results already sent to the output
*/
protected $testFlushIndex = 0;
/**
* @var array<int, array> Buffer for test results
*/
protected $testResults = [];
/**
* @var array<string, int> Lookup table for testname to testResults[index]
*/
protected $testNameResultIndex = [];
/**
* @var bool
*/
protected $enableOutputBuffer = false;
/**
* @var array array<string>
*/
protected $originalExecutionOrder = [];
/**
* @var int
*/
protected $spinState = 0;
/**
* @var bool
*/
protected $showProgress = true;
/**
* @param null|resource|string $out
*
* @throws \PHPUnit\Framework\Exception
*/
public function __construct($out = null, bool $verbose = false, string $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false)
{
parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse);
$this->prettifier = new NamePrettifier($this->colors);
}
public function setOriginalExecutionOrder(array $order): void
{
$this->originalExecutionOrder = $order;
$this->enableOutputBuffer = !empty($order);
}
public function setShowProgressAnimation(bool $showProgress): void
{
$this->showProgress = $showProgress;
}
public function printResult(TestResult $result): void
{
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function endTest(Test $test, float $time): void
{
if (!$test instanceof TestCase && !$test instanceof PhptTestCase && !$test instanceof TestSuite) {
return;
}
if ($this->testHasPassed()) {
$this->registerTestResult($test, null, BaseTestRunner::STATUS_PASSED, $time, false);
}
if ($test instanceof TestCase || $test instanceof PhptTestCase) {
$this->testIndex++;
}
parent::endTest($test, $time);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function addError(Test $test, \Throwable $t, float $time): void
{
$this->registerTestResult($test, $t, BaseTestRunner::STATUS_ERROR, $time, true);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
$this->registerTestResult($test, $e, BaseTestRunner::STATUS_WARNING, $time, true);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->registerTestResult($test, $e, BaseTestRunner::STATUS_FAILURE, $time, true);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
{
$this->registerTestResult($test, $t, BaseTestRunner::STATUS_INCOMPLETE, $time, false);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
{
$this->registerTestResult($test, $t, BaseTestRunner::STATUS_RISKY, $time, false);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
{
$this->registerTestResult($test, $t, BaseTestRunner::STATUS_SKIPPED, $time, false);
}
public function writeProgress(string $progress): void
{
$this->flushOutputBuffer();
}
public function flush(): void
{
$this->flushOutputBuffer(true);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function registerTestResult(Test $test, ?\Throwable $t, int $status, float $time, bool $verbose): void
{
$testName = TestSuiteSorter::getTestSorterUID($test);
$result = [
'className' => $this->formatClassName($test),
'testName' => $testName,
'testMethod' => $this->formatTestName($test),
'message' => '',
'status' => $status,
'time' => $time,
'verbose' => $verbose,
];
if ($t !== null) {
$result['message'] = $this->formatTestResultMessage($t, $result);
}
$this->testResults[$this->testIndex] = $result;
$this->testNameResultIndex[$testName] = $this->testIndex;
}
protected function formatTestName(Test $test): string
{
return $test->getName();
}
protected function formatClassName(Test $test): string
{
return \get_class($test);
}
protected function testHasPassed(): bool
{
if (!isset($this->testResults[$this->testIndex]['status'])) {
return true;
}
if ($this->testResults[$this->testIndex]['status'] === BaseTestRunner::STATUS_PASSED) {
return true;
}
return false;
}
protected function flushOutputBuffer(bool $forceFlush = false): void
{
if ($this->testFlushIndex === $this->testIndex) {
return;
}
if ($this->testFlushIndex > 0) {
if ($this->enableOutputBuffer) {
$prevResult = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex - 1]);
} else {
$prevResult = $this->testResults[$this->testFlushIndex - 1];
}
} else {
$prevResult = $this->getEmptyTestResult();
}
if (!$this->enableOutputBuffer) {
$this->writeTestResult($prevResult, $this->testResults[$this->testFlushIndex++]);
} else {
do {
$flushed = false;
if (!$forceFlush && isset($this->originalExecutionOrder[$this->testFlushIndex])) {
$result = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex]);
} else {
// This test(name) cannot found in original execution order,
// flush result to output stream right away
$result = $this->testResults[$this->testFlushIndex];
}
if (!empty($result)) {
$this->hideSpinner();
$this->writeTestResult($prevResult, $result);
$this->testFlushIndex++;
$prevResult = $result;
$flushed = true;
} else {
$this->showSpinner();
}
} while ($flushed && $this->testFlushIndex < $this->testIndex);
}
}
protected function showSpinner(): void
{
if (!$this->showProgress) {
return;
}
if ($this->spinState) {
$this->undrawSpinner();
}
$this->spinState++;
$this->drawSpinner();
}
protected function hideSpinner(): void
{
if (!$this->showProgress) {
return;
}
if ($this->spinState) {
$this->undrawSpinner();
}
$this->spinState = 0;
}
protected function drawSpinner(): void
{
// optional for CLI printers: show the user a 'buffering output' spinner
}
protected function undrawSpinner(): void
{
// remove the spinner from the current line
}
protected function writeTestResult(array $prevResult, array $result): void
{
}
protected function getEmptyTestResult(): array
{
return [
'className' => '',
'testName' => '',
'message' => '',
'failed' => '',
'verbose' => '',
];
}
protected function getTestResultByName(?string $testName): array
{
if (isset($this->testNameResultIndex[$testName])) {
return $this->testResults[$this->testNameResultIndex[$testName]];
}
return [];
}
protected function formatThrowable(\Throwable $t, ?int $status = null): string
{
$message = \trim(\PHPUnit\Framework\TestFailure::exceptionToString($t));
if ($message) {
$message .= \PHP_EOL . \PHP_EOL . $this->formatStacktrace($t);
} else {
$message = $this->formatStacktrace($t);
}
return $message;
}
protected function formatStacktrace(\Throwable $t): string
{
return \PHPUnit\Util\Filter::getFilteredStacktrace($t);
}
protected function formatTestResultMessage(\Throwable $t, array $result, string $prefix = '│'): string
{
$message = $this->formatThrowable($t, $result['status']);
if ($message === '') {
return '';
}
if (!($this->verbose || $result['verbose'])) {
return '';
}
return $this->prefixLines($prefix, $message);
}
protected function prefixLines(string $prefix, string $message): string
{
$message = \trim($message);
return \implode(
\PHP_EOL,
\array_map(
static function (string $text) use ($prefix) {
return ' ' . $prefix . ($text ? ' ' . $text : '');
},
\preg_split('/\r\n|\r|\n/', $message)
)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class HtmlResultPrinter extends ResultPrinter
{
/**
* @var string
*/
private const PAGE_HEADER = <<<EOT
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Test Documentation</title>
<style>
body {
text-rendering: optimizeLegibility;
font-variant-ligatures: common-ligatures;
font-kerning: normal;
margin-left: 2em;
}
body > ul > li {
font-family: Source Serif Pro, PT Sans, Trebuchet MS, Helvetica, Arial;
font-size: 2em;
}
h2 {
font-family: Tahoma, Helvetica, Arial;
font-size: 3em;
}
ul {
list-style: none;
margin-bottom: 1em;
}
</style>
</head>
<body>
EOT;
/**
* @var string
*/
private const CLASS_HEADER = <<<EOT
<h2 id="%s">%s</h2>
<ul>
EOT;
/**
* @var string
*/
private const CLASS_FOOTER = <<<EOT
</ul>
EOT;
/**
* @var string
*/
private const PAGE_FOOTER = <<<EOT
</body>
</html>
EOT;
/**
* Handler for 'start run' event.
*/
protected function startRun(): void
{
$this->write(self::PAGE_HEADER);
}
/**
* Handler for 'start class' event.
*/
protected function startClass(string $name): void
{
$this->write(
\sprintf(
self::CLASS_HEADER,
$name,
$this->currentTestClassPrettified
)
);
}
/**
* Handler for 'on test' event.
*/
protected function onTest($name, bool $success = true): void
{
$this->write(
\sprintf(
" <li style=\"color: %s;\">%s %s</li>\n",
$success ? '#555753' : '#ef2929',
$success ? '✓' : '❌',
$name
)
);
}
/**
* Handler for 'end class' event.
*/
protected function endClass(string $name): void
{
$this->write(self::CLASS_FOOTER);
}
/**
* Handler for 'end run' event.
*/
protected function endRun(): void
{
$this->write(self::PAGE_FOOTER);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestResult;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\Color;
use SebastianBergmann\Timer\Timer;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class CliTestDoxPrinter extends TestDoxPrinter
{
/**
* The default Testdox left margin for messages is a vertical line
*/
private const PREFIX_SIMPLE = [
'default' => '│',
'start' => '│',
'message' => '│',
'diff' => '│',
'trace' => '│',
'last' => '│',
];
/**
* Colored Testdox use box-drawing for a more textured map of the message
*/
private const PREFIX_DECORATED = [
'default' => '│',
'start' => '┐',
'message' => '├',
'diff' => '┊',
'trace' => '╵',
'last' => '┴',
];
private const SPINNER_ICONS = [
" \e[36m◐\e[0m running tests",
" \e[36m◓\e[0m running tests",
" \e[36m◑\e[0m running tests",
" \e[36m◒\e[0m running tests",
];
private const STATUS_STYLES = [
BaseTestRunner::STATUS_PASSED => [
'symbol' => '✔',
'color' => 'fg-green',
],
BaseTestRunner::STATUS_ERROR => [
'symbol' => '✘',
'color' => 'fg-yellow',
'message' => 'bg-yellow,fg-black',
],
BaseTestRunner::STATUS_FAILURE => [
'symbol' => '✘',
'color' => 'fg-red',
'message' => 'bg-red,fg-white',
],
BaseTestRunner::STATUS_SKIPPED => [
'symbol' => '↩',
'color' => 'fg-cyan',
'message' => 'fg-cyan',
],
BaseTestRunner::STATUS_RISKY => [
'symbol' => '☢',
'color' => 'fg-yellow',
'message' => 'fg-yellow',
],
BaseTestRunner::STATUS_INCOMPLETE => [
'symbol' => '∅',
'color' => 'fg-yellow',
'message' => 'fg-yellow',
],
BaseTestRunner::STATUS_WARNING => [
'symbol' => '⚠',
'color' => 'fg-yellow',
'message' => 'fg-yellow',
],
BaseTestRunner::STATUS_UNKNOWN => [
'symbol' => '?',
'color' => 'fg-blue',
'message' => 'fg-white,bg-blue',
],
];
/**
* @var int[]
*/
private $nonSuccessfulTestResults = [];
/**
* @throws \SebastianBergmann\Timer\RuntimeException
*/
public function printResult(TestResult $result): void
{
$this->printHeader();
$this->printNonSuccessfulTestsSummary($result->count());
$this->printFooter($result);
}
/**
* @throws \SebastianBergmann\Timer\RuntimeException
*/
protected function printHeader(): void
{
$this->write("\n" . Timer::resourceUsage() . "\n\n");
}
protected function formatClassName(Test $test): string
{
if ($test instanceof TestCase) {
return $this->prettifier->prettifyTestClass(\get_class($test));
}
return \get_class($test);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function registerTestResult(Test $test, ?\Throwable $t, int $status, float $time, bool $verbose): void
{
if ($status !== BaseTestRunner::STATUS_PASSED) {
$this->nonSuccessfulTestResults[] = $this->testIndex;
}
parent::registerTestResult($test, $t, $status, $time, $verbose);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function formatTestName(Test $test): string
{
if ($test instanceof TestCase) {
return $this->prettifier->prettifyTestCase($test);
}
return parent::formatTestName($test);
}
protected function writeTestResult(array $prevResult, array $result): void
{
// spacer line for new suite headers and after verbose messages
if ($prevResult['testName'] !== '' &&
(!empty($prevResult['message']) || $prevResult['className'] !== $result['className'])) {
$this->write(\PHP_EOL);
}
// suite header
if ($prevResult['className'] !== $result['className']) {
$this->write($this->colorizeTextBox('underlined', $result['className']) . \PHP_EOL);
}
// test result line
if ($this->colors && $result['className'] === PhptTestCase::class) {
$testName = Color::colorizePath($result['testName'], $prevResult['testName'], true);
} else {
$testName = $result['testMethod'];
}
$style = self::STATUS_STYLES[$result['status']];
$line = \sprintf(
' %s %s%s' . \PHP_EOL,
$this->colorizeTextBox($style['color'], $style['symbol']),
$testName,
$this->verbose ? ' ' . $this->formatRuntime($result['time'], $style['color']) : ''
);
$this->write($line);
// additional information when verbose
$this->write($result['message']);
}
protected function formatThrowable(\Throwable $t, ?int $status = null): string
{
return \trim(\PHPUnit\Framework\TestFailure::exceptionToString($t));
}
protected function colorizeMessageAndDiff(string $style, string $buffer): array
{
$lines = $buffer ? \array_map('\rtrim', \explode(\PHP_EOL, $buffer)) : [];
$message = [];
$diff = [];
$insideDiff = false;
foreach ($lines as $line) {
if ($line === '--- Expected') {
$insideDiff = true;
}
if (!$insideDiff) {
$message[] = $line;
} else {
if (\strpos($line, '-') === 0) {
$line = Color::colorize('fg-red', Color::visualizeWhitespace($line, true));
} elseif (\strpos($line, '+') === 0) {
$line = Color::colorize('fg-green', Color::visualizeWhitespace($line, true));
} elseif ($line === '@@ @@') {
$line = Color::colorize('fg-cyan', $line);
}
$diff[] = $line;
}
}
$diff = \implode(\PHP_EOL, $diff);
if (!empty($message)) {
$message = $this->colorizeTextBox($style, \implode(\PHP_EOL, $message));
}
return [$message, $diff];
}
protected function formatStacktrace(\Throwable $t): string
{
$trace = \PHPUnit\Util\Filter::getFilteredStacktrace($t);
if (!$this->colors) {
return $trace;
}
$lines = [];
$prevPath = '';
foreach (\explode(\PHP_EOL, $trace) as $line) {
if (\preg_match('/^(.*):(\d+)$/', $line, $matches)) {
$lines[] = Color::colorizePath($matches[1], $prevPath) .
Color::dim(':') .
Color::colorize('fg-blue', $matches[2]) .
"\n";
$prevPath = $matches[1];
} else {
$lines[] = $line;
$prevPath = '';
}
}
return \implode('', $lines);
}
protected function formatTestResultMessage(\Throwable $t, array $result, ?string $prefix = null): string
{
$message = $this->formatThrowable($t, $result['status']);
$diff = '';
if (!($this->verbose || $result['verbose'])) {
return '';
}
if ($message && $this->colors) {
$style = self::STATUS_STYLES[$result['status']]['message'] ?? '';
[$message, $diff] = $this->colorizeMessageAndDiff($style, $message);
}
if ($prefix === null || !$this->colors) {
$prefix = self::PREFIX_SIMPLE;
}
if ($this->colors) {
$color = self::STATUS_STYLES[$result['status']]['color'] ?? '';
$prefix = \array_map(static function ($p) use ($color) {
return Color::colorize($color, $p);
}, self::PREFIX_DECORATED);
}
$trace = $this->formatStacktrace($t);
$out = $this->prefixLines($prefix['start'], \PHP_EOL) . \PHP_EOL;
if ($message) {
$out .= $this->prefixLines($prefix['message'], $message . \PHP_EOL) . \PHP_EOL;
}
if ($diff) {
$out .= $this->prefixLines($prefix['diff'], $diff . \PHP_EOL) . \PHP_EOL;
}
if ($trace) {
if ($message || $diff) {
$out .= $this->prefixLines($prefix['default'], \PHP_EOL) . \PHP_EOL;
}
$out .= $this->prefixLines($prefix['trace'], $trace . \PHP_EOL) . \PHP_EOL;
}
$out .= $this->prefixLines($prefix['last'], \PHP_EOL) . \PHP_EOL;
return $out;
}
protected function drawSpinner(): void
{
if ($this->colors) {
$id = $this->spinState % \count(self::SPINNER_ICONS);
$this->write(self::SPINNER_ICONS[$id]);
}
}
protected function undrawSpinner(): void
{
if ($this->colors) {
$id = $this->spinState % \count(self::SPINNER_ICONS);
$this->write("\e[1K\e[" . \strlen(self::SPINNER_ICONS[$id]) . 'D');
}
}
private function formatRuntime(float $time, string $color = ''): string
{
if (!$this->colors) {
return \sprintf('[%.2f ms]', $time * 1000);
}
if ($time > 1) {
$color = 'fg-magenta';
}
return Color::colorize($color, ' ' . (int) \ceil($time * 1000) . ' ' . Color::dim('ms'));
}
private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests): void
{
if (empty($this->nonSuccessfulTestResults)) {
return;
}
if ((\count($this->nonSuccessfulTestResults) / $numberOfExecutedTests) >= 0.7) {
return;
}
$this->write("Summary of non-successful tests:\n\n");
$prevResult = $this->getEmptyTestResult();
foreach ($this->nonSuccessfulTestResults as $testIndex) {
$result = $this->testResults[$testIndex];
$this->writeTestResult($prevResult, $result);
$prevResult = $result;
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
use PHPUnit\Framework\TestCase;
use PHPUnit\Util\Color;
use PHPUnit\Util\Exception as UtilException;
use PHPUnit\Util\Test;
use SebastianBergmann\Exporter\Exporter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class NamePrettifier
{
/**
* @var string[]
*/
private $strings = [];
/**
* @var bool
*/
private $useColor;
public function __construct($useColor = false)
{
$this->useColor = $useColor;
}
/**
* Prettifies the name of a test class.
*
* @psalm-param class-string $className
*/
public function prettifyTestClass(string $className): string
{
try {
$annotations = Test::parseTestMethodAnnotations($className);
if (isset($annotations['class']['testdox'][0])) {
return $annotations['class']['testdox'][0];
}
} catch (UtilException $e) {
}
$parts = \explode('\\', $className);
$className = \array_pop($parts);
if (\substr($className, -1 * \strlen('Test')) === 'Test') {
$className = \substr($className, 0, \strlen($className) - \strlen('Test'));
}
if (\strpos($className, 'Tests') === 0) {
$className = \substr($className, \strlen('Tests'));
} elseif (\strpos($className, 'Test') === 0) {
$className = \substr($className, \strlen('Test'));
}
if (!empty($parts)) {
$parts[] = $className;
$fullyQualifiedName = \implode('\\', $parts);
} else {
$fullyQualifiedName = $className;
}
$result = '';
$wasLowerCase = false;
foreach (\range(0, \strlen($className) - 1) as $i) {
$isLowerCase = \mb_strtolower($className[$i], 'UTF-8') === $className[$i];
if ($wasLowerCase && !$isLowerCase) {
$result .= ' ';
}
$result .= $className[$i];
if ($isLowerCase) {
$wasLowerCase = true;
} else {
$wasLowerCase = false;
}
}
if ($fullyQualifiedName !== $className) {
return $result . ' (' . $fullyQualifiedName . ')';
}
return $result;
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function prettifyTestCase(TestCase $test): string
{
$annotations = $test->getAnnotations();
$annotationWithPlaceholders = false;
$callback = static function (string $variable): string {
return \sprintf('/%s(?=\b)/', \preg_quote($variable, '/'));
};
if (isset($annotations['method']['testdox'][0])) {
$result = $annotations['method']['testdox'][0];
if (\strpos($result, '$') !== false) {
$annotation = $annotations['method']['testdox'][0];
$providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test);
$variables = \array_map($callback, \array_keys($providedData));
$result = \trim(\preg_replace($variables, $providedData, $annotation));
$annotationWithPlaceholders = true;
}
} else {
$result = $this->prettifyTestMethod($test->getName(false));
}
if (!$annotationWithPlaceholders && $test->usesDataProvider()) {
$result .= $this->prettifyDataSet($test);
}
return $result;
}
public function prettifyDataSet(TestCase $test): string
{
if (!$this->useColor) {
return $test->getDataSetAsString(false);
}
if (\is_int($test->dataName())) {
$data = Color::dim(' with data set ') . Color::colorize('fg-cyan', (string) $test->dataName());
} else {
$data = Color::dim(' with ') . Color::colorize('fg-cyan', Color::visualizeWhitespace($test->dataName()));
}
return $data;
}
/**
* Prettifies the name of a test method.
*/
public function prettifyTestMethod(string $name): string
{
$buffer = '';
if ($name === '') {
return $buffer;
}
$string = (string) \preg_replace('#\d+$#', '', $name, -1, $count);
if (\in_array($string, $this->strings)) {
$name = $string;
} elseif ($count === 0) {
$this->strings[] = $string;
}
if (\strpos($name, 'test_') === 0) {
$name = \substr($name, 5);
} elseif (\strpos($name, 'test') === 0) {
$name = \substr($name, 4);
}
if ($name === '') {
return $buffer;
}
$name[0] = \strtoupper($name[0]);
if (\strpos($name, '_') !== false) {
return \trim(\str_replace('_', ' ', $name));
}
$wasNumeric = false;
foreach (\range(0, \strlen($name) - 1) as $i) {
if ($i > 0 && \ord($name[$i]) >= 65 && \ord($name[$i]) <= 90) {
$buffer .= ' ' . \strtolower($name[$i]);
} else {
$isNumeric = \is_numeric($name[$i]);
if (!$wasNumeric && $isNumeric) {
$buffer .= ' ';
$wasNumeric = true;
}
if ($wasNumeric && !$isNumeric) {
$wasNumeric = false;
}
$buffer .= $name[$i];
}
}
return $buffer;
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test): array
{
try {
$reflector = new \ReflectionMethod(\get_class($test), $test->getName(false));
} catch (\ReflectionException $e) {
throw new UtilException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$providedData = [];
$providedDataValues = \array_values($test->getProvidedData());
$i = 0;
$providedData['$_dataName'] = $test->dataName();
foreach ($reflector->getParameters() as $parameter) {
if (!\array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) {
try {
$providedDataValues[$i] = $parameter->getDefaultValue();
} catch (\ReflectionException $e) {
throw new UtilException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
}
$value = $providedDataValues[$i++] ?? null;
if (\is_object($value)) {
$reflector = new \ReflectionObject($value);
if ($reflector->hasMethod('__toString')) {
$value = (string) $value;
} else {
$value = \get_class($value);
}
}
if (!\is_scalar($value)) {
$value = \gettype($value);
}
if (\is_bool($value) || \is_int($value) || \is_float($value)) {
$value = (new Exporter)->export($value);
}
if (\is_string($value) && $value === '') {
if ($this->useColor) {
$value = Color::colorize('dim,underlined', 'empty');
} else {
$value = "''";
}
}
$providedData['$' . $parameter->getName()] = $value;
}
if ($this->useColor) {
$providedData = \array_map(static function ($value) {
return Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, true));
}, $providedData);
}
return $providedData;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
use DOMDocument;
use DOMElement;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Printer;
use ReflectionClass;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class XmlResultPrinter extends Printer implements TestListener
{
/**
* @var DOMDocument
*/
private $document;
/**
* @var DOMElement
*/
private $root;
/**
* @var NamePrettifier
*/
private $prettifier;
/**
* @var null|\Throwable
*/
private $exception;
/**
* @param resource|string $out
*
* @throws Exception
*/
public function __construct($out = null)
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;
$this->root = $this->document->createElement('tests');
$this->document->appendChild($this->root);
$this->prettifier = new NamePrettifier;
parent::__construct($out);
}
/**
* Flush buffer and close output.
*/
public function flush(): void
{
$this->write($this->document->saveXML());
parent::flush();
}
/**
* An error occurred.
*/
public function addError(Test $test, \Throwable $t, float $time): void
{
$this->exception = $t;
}
/**
* A warning occurred.
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
}
/**
* A failure occurred.
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->exception = $e;
}
/**
* Incomplete test.
*/
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
{
}
/**
* Risky test.
*/
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
{
}
/**
* Skipped test.
*/
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
{
}
/**
* A test suite started.
*/
public function startTestSuite(TestSuite $suite): void
{
}
/**
* A test suite ended.
*/
public function endTestSuite(TestSuite $suite): void
{
}
/**
* A test started.
*/
public function startTest(Test $test): void
{
$this->exception = null;
}
/**
* A test ended.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function endTest(Test $test, float $time): void
{
if (!$test instanceof TestCase) {
return;
}
$groups = \array_filter(
$test->getGroups(),
static function ($group) {
return !($group === 'small' || $group === 'medium' || $group === 'large');
}
);
$testNode = $this->document->createElement('test');
$testNode->setAttribute('className', \get_class($test));
$testNode->setAttribute('methodName', $test->getName());
$testNode->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(\get_class($test)));
$testNode->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestCase($test));
$testNode->setAttribute('status', (string) $test->getStatus());
$testNode->setAttribute('time', (string) $time);
$testNode->setAttribute('size', (string) $test->getSize());
$testNode->setAttribute('groups', \implode(',', $groups));
foreach ($groups as $group) {
$groupNode = $this->document->createElement('group');
$groupNode->setAttribute('name', $group);
$testNode->appendChild($groupNode);
}
$annotations = $test->getAnnotations();
foreach (['class', 'method'] as $type) {
foreach ($annotations[$type] as $annotation => $values) {
if ($annotation !== 'covers' && $annotation !== 'uses') {
continue;
}
foreach ($values as $value) {
$coversNode = $this->document->createElement($annotation);
$coversNode->setAttribute('target', $value);
$testNode->appendChild($coversNode);
}
}
}
foreach ($test->doubledTypes() as $doubledType) {
$testDoubleNode = $this->document->createElement('testDouble');
$testDoubleNode->setAttribute('type', $doubledType);
$testNode->appendChild($testDoubleNode);
}
$inlineAnnotations = \PHPUnit\Util\Test::getInlineAnnotations(\get_class($test), $test->getName(false));
if (isset($inlineAnnotations['given'], $inlineAnnotations['when'], $inlineAnnotations['then'])) {
$testNode->setAttribute('given', $inlineAnnotations['given']['value']);
$testNode->setAttribute('givenStartLine', (string) $inlineAnnotations['given']['line']);
$testNode->setAttribute('when', $inlineAnnotations['when']['value']);
$testNode->setAttribute('whenStartLine', (string) $inlineAnnotations['when']['line']);
$testNode->setAttribute('then', $inlineAnnotations['then']['value']);
$testNode->setAttribute('thenStartLine', (string) $inlineAnnotations['then']['line']);
}
if ($this->exception !== null) {
if ($this->exception instanceof Exception) {
$steps = $this->exception->getSerializableTrace();
} else {
$steps = $this->exception->getTrace();
}
try {
$file = (new ReflectionClass($test))->getFileName();
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
foreach ($steps as $step) {
if (isset($step['file']) && $step['file'] === $file) {
$testNode->setAttribute('exceptionLine', (string) $step['line']);
break;
}
}
$testNode->setAttribute('exceptionMessage', $this->exception->getMessage());
}
$this->root->appendChild($testNode);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\TestDox;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Util\Printer;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
abstract class ResultPrinter extends Printer implements TestListener
{
/**
* @var NamePrettifier
*/
protected $prettifier;
/**
* @var string
*/
protected $testClass = '';
/**
* @var int
*/
protected $testStatus;
/**
* @var array
*/
protected $tests = [];
/**
* @var int
*/
protected $successful = 0;
/**
* @var int
*/
protected $warned = 0;
/**
* @var int
*/
protected $failed = 0;
/**
* @var int
*/
protected $risky = 0;
/**
* @var int
*/
protected $skipped = 0;
/**
* @var int
*/
protected $incomplete = 0;
/**
* @var null|string
*/
protected $currentTestClassPrettified;
/**
* @var null|string
*/
protected $currentTestMethodPrettified;
/**
* @var array
*/
private $groups;
/**
* @var array
*/
private $excludeGroups;
/**
* @param resource $out
*
* @throws \PHPUnit\Framework\Exception
*/
public function __construct($out = null, array $groups = [], array $excludeGroups = [])
{
parent::__construct($out);
$this->groups = $groups;
$this->excludeGroups = $excludeGroups;
$this->prettifier = new NamePrettifier;
$this->startRun();
}
/**
* Flush buffer and close output.
*/
public function flush(): void
{
$this->doEndClass();
$this->endRun();
parent::flush();
}
/**
* An error occurred.
*/
public function addError(Test $test, \Throwable $t, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_ERROR;
$this->failed++;
}
/**
* A warning occurred.
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_WARNING;
$this->warned++;
}
/**
* A failure occurred.
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_FAILURE;
$this->failed++;
}
/**
* Incomplete test.
*/
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_INCOMPLETE;
$this->incomplete++;
}
/**
* Risky test.
*/
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_RISKY;
$this->risky++;
}
/**
* Skipped test.
*/
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}
$this->testStatus = BaseTestRunner::STATUS_SKIPPED;
$this->skipped++;
}
/**
* A testsuite started.
*/
public function startTestSuite(TestSuite $suite): void
{
}
/**
* A testsuite ended.
*/
public function endTestSuite(TestSuite $suite): void
{
}
/**
* A test started.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function startTest(Test $test): void
{
if (!$this->isOfInterest($test)) {
return;
}
$class = \get_class($test);
if ($this->testClass !== $class) {
if ($this->testClass !== '') {
$this->doEndClass();
}
$this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class);
$this->testClass = $class;
$this->tests = [];
$this->startClass($class);
}
if ($test instanceof TestCase) {
$this->currentTestMethodPrettified = $this->prettifier->prettifyTestCase($test);
}
$this->testStatus = BaseTestRunner::STATUS_PASSED;
}
/**
* A test ended.
*/
public function endTest(Test $test, float $time): void
{
if (!$this->isOfInterest($test)) {
return;
}
$this->tests[] = [$this->currentTestMethodPrettified, $this->testStatus];
$this->currentTestClassPrettified = null;
$this->currentTestMethodPrettified = null;
}
protected function doEndClass(): void
{
foreach ($this->tests as $test) {
$this->onTest($test[0], $test[1] === BaseTestRunner::STATUS_PASSED);
}
$this->endClass($this->testClass);
}
/**
* Handler for 'start run' event.
*/
protected function startRun(): void
{
}
/**
* Handler for 'start class' event.
*/
protected function startClass(string $name): void
{
}
/**
* Handler for 'on test' event.
*/
protected function onTest($name, bool $success = true): void
{
}
/**
* Handler for 'end class' event.
*/
protected function endClass(string $name): void
{
}
/**
* Handler for 'end run' event.
*/
protected function endRun(): void
{
}
private function isOfInterest(Test $test): bool
{
if (!$test instanceof TestCase) {
return false;
}
if ($test instanceof WarningTestCase) {
return false;
}
if (!empty($this->groups)) {
foreach ($test->getGroups() as $group) {
if (\in_array($group, $this->groups)) {
return true;
}
}
return false;
}
if (!empty($this->excludeGroups)) {
foreach ($test->getGroups() as $group) {
if (\in_array($group, $this->excludeGroups)) {
return false;
}
}
return true;
}
return true;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Filesystem
{
/**
* Maps class names to source file names:
* - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php
* - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php
*/
public static function classNameToFilename(string $className): string
{
return \str_replace(
['_', '\\'],
\DIRECTORY_SEPARATOR,
$className
) . '.php';
}
public static function createDirectory(string $directory): bool
{
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class Printer
{
/**
* If true, flush output after every write.
*
* @var bool
*/
protected $autoFlush = false;
/**
* @var resource
*/
protected $out;
/**
* @var string
*/
protected $outTarget;
/**
* Constructor.
*
* @param null|resource|string $out
*
* @throws Exception
*/
public function __construct($out = null)
{
if ($out === null) {
return;
}
if (\is_string($out) === false) {
$this->out = $out;
return;
}
if (\strpos($out, 'socket://') === 0) {
$out = \explode(':', \str_replace('socket://', '', $out));
if (\count($out) !== 2) {
throw new Exception;
}
$this->out = \fsockopen($out[0], $out[1]);
} else {
if (\strpos($out, 'php://') === false && !Filesystem::createDirectory(\dirname($out))) {
throw new Exception(\sprintf('Directory "%s" was not created', \dirname($out)));
}
$this->out = \fopen($out, 'wt');
}
$this->outTarget = $out;
}
/**
* Flush buffer and close output if it's not to a PHP stream
*/
public function flush(): void
{
if ($this->out && \strncmp($this->outTarget, 'php://', 6) !== 0) {
\fclose($this->out);
}
}
/**
* Performs a safe, incremental flush.
*
* Do not confuse this function with the flush() function of this class,
* since the flush() function may close the file being written to, rendering
* the current object no longer usable.
*/
public function incrementalFlush(): void
{
if ($this->out) {
\fflush($this->out);
} else {
\flush();
}
}
public function write(string $buffer): void
{
if ($this->out) {
\fwrite($this->out, $buffer);
if ($this->autoFlush) {
$this->incrementalFlush();
}
} else {
if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg') {
$buffer = \htmlspecialchars($buffer, \ENT_SUBSTITUTE);
}
print $buffer;
if ($this->autoFlush) {
$this->incrementalFlush();
}
}
}
/**
* Check auto-flush mode.
*/
public function getAutoFlush(): bool
{
return $this->autoFlush;
}
/**
* Set auto-flushing mode.
*
* If set, *incremental* flushes will be done after each write. This should
* not be confused with the different effects of this class' flush() method.
*/
public function setAutoFlush(bool $autoFlush): void
{
$this->autoFlush = $autoFlush;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class FileLoader
{
/**
* Checks if a PHP sourcecode file is readable. The sourcecode file is loaded through the load() method.
*
* As a fallback, PHP looks in the directory of the file executing the stream_resolve_include_path function.
* We do not want to load the Test.php file here, so skip it if it found that.
* PHP prioritizes the include_path setting, so if the current directory is in there, it will first look in the
* current working directory.
*
* @throws Exception
*/
public static function checkAndLoad(string $filename): string
{
$includePathFilename = \stream_resolve_include_path($filename);
if (!$includePathFilename) {
throw new Exception(
\sprintf('Cannot open file "%s".' . "\n", $filename)
);
}
$localFile = __DIR__ . \DIRECTORY_SEPARATOR . $filename;
if ($includePathFilename === $localFile || !self::isReadable($includePathFilename)) {
throw new Exception(
\sprintf('Cannot open file "%s".' . "\n", $filename)
);
}
self::load($includePathFilename);
return $includePathFilename;
}
/**
* Loads a PHP sourcefile.
*/
public static function load(string $filename): void
{
$oldVariableNames = \array_keys(\get_defined_vars());
include_once $filename;
$newVariables = \get_defined_vars();
foreach (\array_diff(\array_keys($newVariables), $oldVariableNames) as $variableName) {
if ($variableName !== 'oldVariableNames') {
$GLOBALS[$variableName] = $newVariables[$variableName];
}
}
}
/**
* @see https://github.com/sebastianbergmann/phpunit/pull/2751
*/
private static function isReadable(string $filename): bool
{
return @\fopen($filename, 'r') !== false;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\Annotation;
use PharIo\Version\VersionConstraintParser;
use PHPUnit\Framework\InvalidDataProviderException;
use PHPUnit\Framework\SkippedTestError;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Exception;
/**
* This is an abstraction around a PHPUnit-specific docBlock,
* allowing us to ask meaningful questions about a specific
* reflection symbol.
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class DocBlock
{
/**
* @todo This constant should be private (it's public because of TestTest::testGetProvidedDataRegEx)
*/
public const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/';
private const REGEX_REQUIRES_VERSION = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<operator>[<>=!]{0,2})\s*(?P<version>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m';
private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<constraint>[\d\t \-.|~^]+)[ \t]*\r?$/m';
private const REGEX_REQUIRES_OS = '/@requires\s+(?P<name>OS(?:FAMILY)?)\s+(?P<value>.+?)[ \t]*\r?$/m';
private const REGEX_REQUIRES_SETTING = '/@requires\s+(?P<name>setting)\s+(?P<setting>([^ ]+?))\s*(?P<value>[\w\.-]+[\w\.]?)?[ \t]*\r?$/m';
private const REGEX_REQUIRES = '/@requires\s+(?P<name>function|extension)\s+(?P<value>([^\s<>=!]+))\s*(?P<operator>[<>=!]{0,2})\s*(?P<version>[\d\.-]+[\d\.]?)?[ \t]*\r?$/m';
private const REGEX_TEST_WITH = '/@testWith\s+/';
private const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m';
/** @var string */
private $docComment;
/** @var bool */
private $isMethod;
/** @var array<string, array<int, string>> pre-parsed annotations indexed by name and occurrence index */
private $symbolAnnotations;
/**
* @var null|array<string, mixed>
*
* @psalm-var null|(array{
* __OFFSET: array<string, int>&array{__FILE: string},
* setting?: array<string, string>,
* extension_versions?: array<string, array{version: string, operator: string}>
* }&array<
* string,
* string|array{version: string, operator: string}|array{constraint: string}|array<int|string, string>
* >)
*/
private $parsedRequirements;
/** @var int */
private $startLine;
/** @var int */
private $endLine;
/** @var string */
private $fileName;
/** @var string */
private $name;
/**
* @var string
*
* @psalm-var class-string
*/
private $className;
public static function ofClass(\ReflectionClass $class): self
{
$className = $class->getName();
return new self(
(string) $class->getDocComment(),
false,
self::extractAnnotationsFromReflector($class),
$class->getStartLine(),
$class->getEndLine(),
$class->getFileName(),
$className,
$className
);
}
/**
* @psalm-param class-string $classNameInHierarchy
*/
public static function ofMethod(\ReflectionMethod $method, string $classNameInHierarchy): self
{
return new self(
(string) $method->getDocComment(),
true,
self::extractAnnotationsFromReflector($method),
$method->getStartLine(),
$method->getEndLine(),
$method->getFileName(),
$method->getName(),
$classNameInHierarchy
);
}
/**
* Note: we do not preserve an instance of the reflection object, since it cannot be safely (de-)serialized.
*
* @param array<string, array<int, string>> $symbolAnnotations
*
* @psalm-param class-string $className
*/
private function __construct(string $docComment, bool $isMethod, array $symbolAnnotations, int $startLine, int $endLine, string $fileName, string $name, string $className)
{
$this->docComment = $docComment;
$this->isMethod = $isMethod;
$this->symbolAnnotations = $symbolAnnotations;
$this->startLine = $startLine;
$this->endLine = $endLine;
$this->fileName = $fileName;
$this->name = $name;
$this->className = $className;
}
/**
* @psalm-return array{
* __OFFSET: array<string, int>&array{__FILE: string},
* setting?: array<string, string>,
* extension_versions?: array<string, array{version: string, operator: string}>
* }&array<
* string,
* string|array{version: string, operator: string}|array{constraint: string}|array<int|string, string>
* >
*
* @throws Warning if the requirements version constraint is not well-formed
*/
public function requirements(): array
{
if ($this->parsedRequirements !== null) {
return $this->parsedRequirements;
}
$offset = $this->startLine;
$requires = [];
$recordedSettings = [];
$extensionVersions = [];
$recordedOffsets = [
'__FILE' => \realpath($this->fileName),
];
// Split docblock into lines and rewind offset to start of docblock
$lines = \preg_split('/\r\n|\r|\n/', $this->docComment);
$offset -= \count($lines);
foreach ($lines as $line) {
if (\preg_match(self::REGEX_REQUIRES_OS, $line, $matches)) {
$requires[$matches['name']] = $matches['value'];
$recordedOffsets[$matches['name']] = $offset;
}
if (\preg_match(self::REGEX_REQUIRES_VERSION, $line, $matches)) {
$requires[$matches['name']] = [
'version' => $matches['version'],
'operator' => $matches['operator'],
];
$recordedOffsets[$matches['name']] = $offset;
}
if (\preg_match(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $line, $matches)) {
if (!empty($requires[$matches['name']])) {
$offset++;
continue;
}
try {
$versionConstraintParser = new VersionConstraintParser;
$requires[$matches['name'] . '_constraint'] = [
'constraint' => $versionConstraintParser->parse(\trim($matches['constraint'])),
];
$recordedOffsets[$matches['name'] . '_constraint'] = $offset;
} catch (\PharIo\Version\Exception $e) {
/* @TODO this catch is currently not valid, see https://github.com/phar-io/version/issues/16 */
throw new Warning($e->getMessage(), $e->getCode(), $e);
}
}
if (\preg_match(self::REGEX_REQUIRES_SETTING, $line, $matches)) {
$recordedSettings[$matches['setting']] = $matches['value'];
$recordedOffsets['__SETTING_' . $matches['setting']] = $offset;
}
if (\preg_match(self::REGEX_REQUIRES, $line, $matches)) {
$name = $matches['name'] . 's';
if (!isset($requires[$name])) {
$requires[$name] = [];
}
$requires[$name][] = $matches['value'];
$recordedOffsets[$matches['name'] . '_' . $matches['value']] = $offset;
if ($name === 'extensions' && !empty($matches['version'])) {
$extensionVersions[$matches['value']] = [
'version' => $matches['version'],
'operator' => $matches['operator'],
];
}
}
$offset++;
}
return $this->parsedRequirements = \array_merge(
$requires,
['__OFFSET' => $recordedOffsets],
\array_filter([
'setting' => $recordedSettings,
'extension_versions' => $extensionVersions,
])
);
}
/**
* @return array|bool
*
* @psalm-return false|array{
* class: class-string,
* code: int|string|null,
* message: string,
* message_regex: string
* }
*/
public function expectedException()
{
$docComment = (string) \substr($this->docComment, 3, -2);
if (1 !== \preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) {
return false;
}
/** @psalm-var class-string $class */
$class = $matches[1];
$annotations = $this->symbolAnnotations();
$code = null;
$message = '';
$messageRegExp = '';
if (isset($matches[2])) {
$message = \trim($matches[2]);
} elseif (isset($annotations['expectedExceptionMessage'])) {
$message = $this->parseAnnotationContent($annotations['expectedExceptionMessage'][0]);
}
if (isset($annotations['expectedExceptionMessageRegExp'])) {
$messageRegExp = $this->parseAnnotationContent($annotations['expectedExceptionMessageRegExp'][0]);
}
if (isset($matches[3])) {
$code = $matches[3];
} elseif (isset($annotations['expectedExceptionCode'])) {
$code = $this->parseAnnotationContent($annotations['expectedExceptionCode'][0]);
}
if (\is_numeric($code)) {
$code = (int) $code;
} elseif (\is_string($code) && \defined($code)) {
$code = (int) \constant($code);
}
return [
'class' => $class,
'code' => $code,
'message' => $message,
'message_regex' => $messageRegExp,
];
}
/**
* Returns the provided data for a method.
*
* @throws Exception
*/
public function getProvidedData(): ?array
{
/** @noinspection SuspiciousBinaryOperationInspection */
$data = $this->getDataFromDataProviderAnnotation($this->docComment) ?? $this->getDataFromTestWithAnnotation($this->docComment);
if ($data === null) {
return null;
}
if ($data === []) {
throw new SkippedTestError;
}
foreach ($data as $key => $value) {
if (!\is_array($value)) {
throw new Exception(
\sprintf(
'Data set %s is invalid.',
\is_int($key) ? '#' . $key : '"' . $key . '"'
)
);
}
}
return $data;
}
/**
* @psalm-return array<string, array{line: int, value: string}>
*/
public function getInlineAnnotations(): array
{
$code = \file($this->fileName);
$lineNumber = $this->startLine;
$startLine = $this->startLine - 1;
$endLine = $this->endLine - 1;
$codeLines = \array_slice($code, $startLine, $endLine - $startLine + 1);
$annotations = [];
foreach ($codeLines as $line) {
if (\preg_match('#/\*\*?\s*@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?\*/$#m', $line, $matches)) {
$annotations[\strtolower($matches['name'])] = [
'line' => $lineNumber,
'value' => $matches['value'],
];
}
$lineNumber++;
}
return $annotations;
}
public function symbolAnnotations(): array
{
return $this->symbolAnnotations;
}
public function isHookToBeExecutedBeforeClass(): bool
{
return $this->isMethod
&& false !== \strpos($this->docComment, '@beforeClass');
}
public function isHookToBeExecutedAfterClass(): bool
{
return $this->isMethod
&& false !== \strpos($this->docComment, '@afterClass');
}
public function isToBeExecutedBeforeTest(): bool
{
return 1 === \preg_match('/@before\b/', $this->docComment);
}
public function isToBeExecutedAfterTest(): bool
{
return 1 === \preg_match('/@after\b/', $this->docComment);
}
/**
* Parse annotation content to use constant/class constant values
*
* Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME
*
* If the constant is not found the string is used as is to ensure maximum BC.
*/
private function parseAnnotationContent(string $message): string
{
if (\defined($message) &&
(\strpos($message, '::') !== false && \substr_count($message, '::') + 1 === 2)) {
return \constant($message);
}
return $message;
}
private function getDataFromDataProviderAnnotation(string $docComment): ?array
{
$methodName = null;
$className = $this->className;
if ($this->isMethod) {
$methodName = $this->name;
}
if (!\preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
return null;
}
$result = [];
foreach ($matches[1] as $match) {
$dataProviderMethodNameNamespace = \explode('\\', $match);
$leaf = \explode('::', \array_pop($dataProviderMethodNameNamespace));
$dataProviderMethodName = \array_pop($leaf);
if (empty($dataProviderMethodNameNamespace)) {
$dataProviderMethodNameNamespace = '';
} else {
$dataProviderMethodNameNamespace = \implode('\\', $dataProviderMethodNameNamespace) . '\\';
}
if (empty($leaf)) {
$dataProviderClassName = $className;
} else {
/** @psalm-var class-string $dataProviderClassName */
$dataProviderClassName = $dataProviderMethodNameNamespace . \array_pop($leaf);
}
try {
$dataProviderClass = new \ReflectionClass($dataProviderClassName);
$dataProviderMethod = $dataProviderClass->getMethod(
$dataProviderMethodName
);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($dataProviderMethod->isStatic()) {
$object = null;
} else {
$object = $dataProviderClass->newInstance();
}
if ($dataProviderMethod->getNumberOfParameters() === 0) {
$data = $dataProviderMethod->invoke($object);
} else {
$data = $dataProviderMethod->invoke($object, $methodName);
}
if ($data instanceof \Traversable) {
$origData = $data;
$data = [];
foreach ($origData as $key => $value) {
if (\is_int($key)) {
$data[] = $value;
} elseif (\array_key_exists($key, $data)) {
throw new InvalidDataProviderException(
\sprintf(
'The key "%s" has already been defined in the data provider "%s".',
$key,
$match
)
);
} else {
$data[$key] = $value;
}
}
}
if (\is_array($data)) {
$result = \array_merge($result, $data);
}
}
return $result;
}
/**
* @throws Exception
*/
private function getDataFromTestWithAnnotation(string $docComment): ?array
{
$docComment = $this->cleanUpMultiLineAnnotation($docComment);
if (!\preg_match(self::REGEX_TEST_WITH, $docComment, $matches, \PREG_OFFSET_CAPTURE)) {
return null;
}
$offset = \strlen($matches[0][0]) + $matches[0][1];
$annotationContent = \substr($docComment, $offset);
$data = [];
foreach (\explode("\n", $annotationContent) as $candidateRow) {
$candidateRow = \trim($candidateRow);
if ($candidateRow[0] !== '[') {
break;
}
$dataSet = \json_decode($candidateRow, true);
if (\json_last_error() !== \JSON_ERROR_NONE) {
throw new Exception(
'The data set for the @testWith annotation cannot be parsed: ' . \json_last_error_msg()
);
}
$data[] = $dataSet;
}
if (!$data) {
throw new Exception('The data set for the @testWith annotation cannot be parsed.');
}
return $data;
}
private function cleanUpMultiLineAnnotation(string $docComment): string
{
//removing initial ' * ' for docComment
$docComment = \str_replace("\r\n", "\n", $docComment);
$docComment = \preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment);
$docComment = (string) \substr($docComment, 0, -1);
return \rtrim($docComment, "\n");
}
/** @return array<string, array<int, string>> */
private static function parseDocBlock(string $docBlock): array
{
// Strip away the docblock header and footer to ease parsing of one line annotations
$docBlock = (string) \substr($docBlock, 3, -2);
$annotations = [];
if (\preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m', $docBlock, $matches)) {
$numMatches = \count($matches[0]);
for ($i = 0; $i < $numMatches; ++$i) {
$annotations[$matches['name'][$i]][] = (string) $matches['value'][$i];
}
}
return $annotations;
}
/** @param \ReflectionClass|\ReflectionFunctionAbstract $reflector */
private static function extractAnnotationsFromReflector(\Reflector $reflector): array
{
$annotations = [];
if ($reflector instanceof \ReflectionClass) {
$annotations = \array_merge(
$annotations,
...\array_map(
function (\ReflectionClass $trait): array {
return self::parseDocBlock((string) $trait->getDocComment());
},
\array_values($reflector->getTraits())
)
);
}
return \array_merge(
$annotations,
self::parseDocBlock((string) $reflector->getDocComment())
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\Annotation;
use PHPUnit\Util\Exception;
/**
* Reflection information, and therefore DocBlock information, is static within
* a single PHP process. It is therefore okay to use a Singleton registry here.
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Registry
{
/** @var null|self */
private static $instance;
/** @var array<string, DocBlock> indexed by class name */
private $classDocBlocks = [];
/** @var array<string, array<string, DocBlock>> indexed by class name and method name */
private $methodDocBlocks = [];
public static function getInstance(): self
{
return self::$instance ?? self::$instance = new self;
}
private function __construct()
{
}
/**
* @throws Exception
* @psalm-param class-string $class
*/
public function forClassName(string $class): DocBlock
{
if (\array_key_exists($class, $this->classDocBlocks)) {
return $this->classDocBlocks[$class];
}
try {
$reflection = new \ReflectionClass($class);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
return $this->classDocBlocks[$class] = DocBlock::ofClass($reflection);
}
/**
* @throws Exception
* @psalm-param class-string $classInHierarchy
*/
public function forMethod(string $classInHierarchy, string $method): DocBlock
{
if (isset($this->methodDocBlocks[$classInHierarchy][$method])) {
return $this->methodDocBlocks[$classInHierarchy][$method];
}
try {
$reflection = new \ReflectionMethod($classInHierarchy, $method);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
return $this->methodDocBlocks[$classInHierarchy][$method] = DocBlock::ofMethod($reflection, $classInHierarchy);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Color
{
/**
* @var array<string,string>
*/
private const WHITESPACE_MAP = [
' ' => '·',
"\t" => '⇥',
];
/**
* @var array<string,string>
*/
private const WHITESPACE_EOL_MAP = [
' ' => '·',
"\t" => '⇥',
"\n" => '↵',
"\r" => '⟵',
];
/**
* @var array<string,string>
*/
private static $ansiCodes = [
'reset' => '0',
'bold' => '1',
'dim' => '2',
'dim-reset' => '22',
'underlined' => '4',
'fg-default' => '39',
'fg-black' => '30',
'fg-red' => '31',
'fg-green' => '32',
'fg-yellow' => '33',
'fg-blue' => '34',
'fg-magenta' => '35',
'fg-cyan' => '36',
'fg-white' => '37',
'bg-default' => '49',
'bg-black' => '40',
'bg-red' => '41',
'bg-green' => '42',
'bg-yellow' => '43',
'bg-blue' => '44',
'bg-magenta' => '45',
'bg-cyan' => '46',
'bg-white' => '47',
];
public static function colorize(string $color, string $buffer): string
{
if (\trim($buffer) === '') {
return $buffer;
}
$codes = \array_map('\trim', \explode(',', $color));
$styles = [];
foreach ($codes as $code) {
if (isset(self::$ansiCodes[$code])) {
$styles[] = self::$ansiCodes[$code] ?? '';
}
}
if (empty($styles)) {
return $buffer;
}
return self::optimizeColor(\sprintf("\x1b[%sm", \implode(';', $styles)) . $buffer . "\x1b[0m");
}
public static function colorizePath(string $path, ?string $prevPath = null, bool $colorizeFilename = false): string
{
if ($prevPath === null) {
$prevPath = '';
}
$path = \explode(\DIRECTORY_SEPARATOR, $path);
$prevPath = \explode(\DIRECTORY_SEPARATOR, $prevPath);
for ($i = 0; $i < \min(\count($path), \count($prevPath)); $i++) {
if ($path[$i] == $prevPath[$i]) {
$path[$i] = self::dim($path[$i]);
}
}
if ($colorizeFilename) {
$last = \count($path) - 1;
$path[$last] = \preg_replace_callback(
'/([\-_\.]+|phpt$)/',
static function ($matches) {
return self::dim($matches[0]);
},
$path[$last]
);
}
return self::optimizeColor(\implode(self::dim(\DIRECTORY_SEPARATOR), $path));
}
public static function dim(string $buffer): string
{
if (\trim($buffer) === '') {
return $buffer;
}
return "\e[2m$buffer\e[22m";
}
public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = false): string
{
$replaceMap = $visualizeEOL ? self::WHITESPACE_EOL_MAP : self::WHITESPACE_MAP;
return \preg_replace_callback('/\s+/', static function ($matches) use ($replaceMap) {
return self::dim(\strtr($matches[0], $replaceMap));
}, $buffer);
}
private static function optimizeColor(string $buffer): string
{
$patterns = [
"/\e\\[22m\e\\[2m/" => '',
"/\e\\[([^m]*)m\e\\[([1-9][0-9;]*)m/" => "\e[$1;$2m",
"/(\e\\[[^m]*m)+(\e\\[0m)/" => '$2',
];
return \preg_replace(\array_keys($patterns), \array_values($patterns), $buffer);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use DOMCharacterData;
use DOMDocument;
use DOMElement;
use DOMNode;
use DOMText;
use PHPUnit\Framework\Exception;
use ReflectionClass;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Xml
{
public static function import(DOMElement $element): DOMElement
{
return (new DOMDocument)->importNode($element, true);
}
/**
* Load an $actual document into a DOMDocument. This is called
* from the selector assertions.
*
* If $actual is already a DOMDocument, it is returned with
* no changes. Otherwise, $actual is loaded into a new DOMDocument
* as either HTML or XML, depending on the value of $isHtml. If $isHtml is
* false and $xinclude is true, xinclude is performed on the loaded
* DOMDocument.
*
* Note: prior to PHPUnit 3.3.0, this method loaded a file and
* not a string as it currently does. To load a file into a
* DOMDocument, use loadFile() instead.
*
* @param DOMDocument|string $actual
*
* @throws Exception
*/
public static function load($actual, bool $isHtml = false, string $filename = '', bool $xinclude = false, bool $strict = false): DOMDocument
{
if ($actual instanceof DOMDocument) {
return $actual;
}
if (!\is_string($actual)) {
throw new Exception('Could not load XML from ' . \gettype($actual));
}
if ($actual === '') {
throw new Exception('Could not load XML from empty string');
}
// Required for XInclude on Windows.
if ($xinclude) {
$cwd = \getcwd();
@\chdir(\dirname($filename));
}
$document = new DOMDocument;
$document->preserveWhiteSpace = false;
$internal = \libxml_use_internal_errors(true);
$message = '';
$reporting = \error_reporting(0);
if ($filename !== '') {
// Required for XInclude
$document->documentURI = $filename;
}
if ($isHtml) {
$loaded = $document->loadHTML($actual);
} else {
$loaded = $document->loadXML($actual);
}
if (!$isHtml && $xinclude) {
$document->xinclude();
}
foreach (\libxml_get_errors() as $error) {
$message .= "\n" . $error->message;
}
\libxml_use_internal_errors($internal);
\error_reporting($reporting);
if (isset($cwd)) {
@\chdir($cwd);
}
if ($loaded === false || ($strict && $message !== '')) {
if ($filename !== '') {
throw new Exception(
\sprintf(
'Could not load "%s".%s',
$filename,
$message !== '' ? "\n" . $message : ''
)
);
}
if ($message === '') {
$message = 'Could not load XML for unknown reason';
}
throw new Exception($message);
}
return $document;
}
/**
* Loads an XML (or HTML) file into a DOMDocument object.
*
* @throws Exception
*/
public static function loadFile(string $filename, bool $isHtml = false, bool $xinclude = false, bool $strict = false): DOMDocument
{
$reporting = \error_reporting(0);
$contents = \file_get_contents($filename);
\error_reporting($reporting);
if ($contents === false) {
throw new Exception(
\sprintf(
'Could not read "%s".',
$filename
)
);
}
return self::load($contents, $isHtml, $filename, $xinclude, $strict);
}
public static function removeCharacterDataNodes(DOMNode $node): void
{
if ($node->hasChildNodes()) {
for ($i = $node->childNodes->length - 1; $i >= 0; $i--) {
if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) {
$node->removeChild($child);
}
}
}
}
/**
* Escapes a string for the use in XML documents
*
* Any Unicode character is allowed, excluding the surrogate blocks, FFFE,
* and FFFF (not even as character reference).
*
* @see https://www.w3.org/TR/xml/#charsets
*/
public static function prepareString(string $string): string
{
return \preg_replace(
'/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/',
'',
\htmlspecialchars(
self::convertToUtf8($string),
\ENT_QUOTES
)
);
}
/**
* "Convert" a DOMElement object into a PHP variable.
*/
public static function xmlToVariable(DOMElement $element)
{
$variable = null;
switch ($element->tagName) {
case 'array':
$variable = [];
foreach ($element->childNodes as $entry) {
if (!$entry instanceof DOMElement || $entry->tagName !== 'element') {
continue;
}
$item = $entry->childNodes->item(0);
if ($item instanceof DOMText) {
$item = $entry->childNodes->item(1);
}
$value = self::xmlToVariable($item);
if ($entry->hasAttribute('key')) {
$variable[(string) $entry->getAttribute('key')] = $value;
} else {
$variable[] = $value;
}
}
break;
case 'object':
$className = $element->getAttribute('class');
if ($element->hasChildNodes()) {
$arguments = $element->childNodes->item(0)->childNodes;
$constructorArgs = [];
foreach ($arguments as $argument) {
if ($argument instanceof DOMElement) {
$constructorArgs[] = self::xmlToVariable($argument);
}
}
try {
$variable = (new ReflectionClass($className))->newInstanceArgs($constructorArgs);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
} else {
$variable = new $className;
}
break;
case 'boolean':
$variable = $element->textContent === 'true';
break;
case 'integer':
case 'double':
case 'string':
$variable = $element->textContent;
\settype($variable, $element->tagName);
break;
}
return $variable;
}
private static function convertToUtf8(string $string): string
{
if (!self::isUtf8($string)) {
$string = \mb_convert_encoding($string, 'UTF-8');
}
return $string;
}
private static function isUtf8(string $string): bool
{
$length = \strlen($string);
for ($i = 0; $i < $length; $i++) {
if (\ord($string[$i]) < 0x80) {
$n = 0;
} elseif ((\ord($string[$i]) & 0xE0) === 0xC0) {
$n = 1;
} elseif ((\ord($string[$i]) & 0xF0) === 0xE0) {
$n = 2;
} elseif ((\ord($string[$i]) & 0xF0) === 0xF0) {
$n = 3;
} else {
return false;
}
for ($j = 0; $j < $n; $j++) {
if ((++$i === $length) || ((\ord($string[$i]) & 0xC0) !== 0x80)) {
return false;
}
}
}
return true;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class XdebugFilterScriptGenerator
{
public function generate(array $filterData): string
{
$items = $this->getWhitelistItems($filterData);
$files = \array_map(
static function ($item) {
return \sprintf(
" '%s'",
$item
);
},
$items
);
$files = \implode(",\n", $files);
return <<<EOF
<?php declare(strict_types=1);
if (!\\function_exists('xdebug_set_filter')) {
return;
}
\\xdebug_set_filter(
\\XDEBUG_FILTER_CODE_COVERAGE,
\\XDEBUG_PATH_WHITELIST,
[
$files
]
);
EOF;
}
private function getWhitelistItems(array $filterData): array
{
$files = [];
if (isset($filterData['include']['directory'])) {
foreach ($filterData['include']['directory'] as $directory) {
$path = \realpath($directory['path']);
if (\is_string($path)) {
$files[] = \sprintf(
\addslashes('%s' . \DIRECTORY_SEPARATOR),
$path
);
}
}
}
if (isset($filterData['include']['directory'])) {
foreach ($filterData['include']['file'] as $file) {
$files[] = $file;
}
}
return $files;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\Log;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExceptionWrapper;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Exception;
use PHPUnit\Util\Filter;
use PHPUnit\Util\Printer;
use PHPUnit\Util\Xml;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class JUnit extends Printer implements TestListener
{
/**
* @var \DOMDocument
*/
private $document;
/**
* @var \DOMElement
*/
private $root;
/**
* @var bool
*/
private $reportRiskyTests = false;
/**
* @var \DOMElement[]
*/
private $testSuites = [];
/**
* @var int[]
*/
private $testSuiteTests = [0];
/**
* @var int[]
*/
private $testSuiteAssertions = [0];
/**
* @var int[]
*/
private $testSuiteErrors = [0];
/**
* @var int[]
*/
private $testSuiteWarnings = [0];
/**
* @var int[]
*/
private $testSuiteFailures = [0];
/**
* @var int[]
*/
private $testSuiteSkipped = [0];
/**
* @var int[]
*/
private $testSuiteTimes = [0];
/**
* @var int
*/
private $testSuiteLevel = 0;
/**
* @var \DOMElement
*/
private $currentTestCase;
/**
* @param null|mixed $out
*/
public function __construct($out = null, bool $reportRiskyTests = false)
{
$this->document = new \DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;
$this->root = $this->document->createElement('testsuites');
$this->document->appendChild($this->root);
parent::__construct($out);
$this->reportRiskyTests = $reportRiskyTests;
}
/**
* Flush buffer and close output.
*/
public function flush(): void
{
$this->write($this->getXML());
parent::flush();
}
/**
* An error occurred.
*/
public function addError(Test $test, \Throwable $t, float $time): void
{
$this->doAddFault($test, $t, $time, 'error');
$this->testSuiteErrors[$this->testSuiteLevel]++;
}
/**
* A warning occurred.
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
$this->doAddFault($test, $e, $time, 'warning');
$this->testSuiteWarnings[$this->testSuiteLevel]++;
}
/**
* A failure occurred.
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->doAddFault($test, $e, $time, 'failure');
$this->testSuiteFailures[$this->testSuiteLevel]++;
}
/**
* Incomplete test.
*/
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
{
$this->doAddSkipped();
}
/**
* Risky test.
*/
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
{
if (!$this->reportRiskyTests || $this->currentTestCase === null) {
return;
}
$error = $this->document->createElement(
'error',
Xml::prepareString(
"Risky Test\n" .
Filter::getFilteredStacktrace($t)
)
);
$error->setAttribute('type', \get_class($t));
$this->currentTestCase->appendChild($error);
$this->testSuiteErrors[$this->testSuiteLevel]++;
}
/**
* Skipped test.
*/
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
{
$this->doAddSkipped();
}
/**
* A testsuite started.
*/
public function startTestSuite(TestSuite $suite): void
{
$testSuite = $this->document->createElement('testsuite');
$testSuite->setAttribute('name', $suite->getName());
if (\class_exists($suite->getName(), false)) {
try {
$class = new \ReflectionClass($suite->getName());
$testSuite->setAttribute('file', $class->getFileName());
} catch (\ReflectionException $e) {
}
}
if ($this->testSuiteLevel > 0) {
$this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
} else {
$this->root->appendChild($testSuite);
}
$this->testSuiteLevel++;
$this->testSuites[$this->testSuiteLevel] = $testSuite;
$this->testSuiteTests[$this->testSuiteLevel] = 0;
$this->testSuiteAssertions[$this->testSuiteLevel] = 0;
$this->testSuiteErrors[$this->testSuiteLevel] = 0;
$this->testSuiteWarnings[$this->testSuiteLevel] = 0;
$this->testSuiteFailures[$this->testSuiteLevel] = 0;
$this->testSuiteSkipped[$this->testSuiteLevel] = 0;
$this->testSuiteTimes[$this->testSuiteLevel] = 0;
}
/**
* A testsuite ended.
*/
public function endTestSuite(TestSuite $suite): void
{
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'tests',
(string) $this->testSuiteTests[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'assertions',
(string) $this->testSuiteAssertions[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'errors',
(string) $this->testSuiteErrors[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'warnings',
(string) $this->testSuiteWarnings[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'failures',
(string) $this->testSuiteFailures[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'skipped',
(string) $this->testSuiteSkipped[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'time',
\sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])
);
if ($this->testSuiteLevel > 1) {
$this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
$this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
$this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
$this->testSuiteWarnings[$this->testSuiteLevel - 1] += $this->testSuiteWarnings[$this->testSuiteLevel];
$this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
$this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel];
$this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
}
$this->testSuiteLevel--;
}
/**
* A test started.
*/
public function startTest(Test $test): void
{
$usesDataprovider = false;
if (\method_exists($test, 'usesDataProvider')) {
$usesDataprovider = $test->usesDataProvider();
}
$testCase = $this->document->createElement('testcase');
$testCase->setAttribute('name', $test->getName());
try {
$class = new \ReflectionClass($test);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$methodName = $test->getName(!$usesDataprovider);
if ($class->hasMethod($methodName)) {
try {
$method = $class->getMethod($methodName);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$testCase->setAttribute('class', $class->getName());
$testCase->setAttribute('classname', \str_replace('\\', '.', $class->getName()));
$testCase->setAttribute('file', $class->getFileName());
$testCase->setAttribute('line', (string) $method->getStartLine());
}
$this->currentTestCase = $testCase;
}
/**
* A test ended.
*/
public function endTest(Test $test, float $time): void
{
$numAssertions = 0;
if (\method_exists($test, 'getNumAssertions')) {
$numAssertions = $test->getNumAssertions();
}
$this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;
$this->currentTestCase->setAttribute(
'assertions',
(string) $numAssertions
);
$this->currentTestCase->setAttribute(
'time',
\sprintf('%F', $time)
);
$this->testSuites[$this->testSuiteLevel]->appendChild(
$this->currentTestCase
);
$this->testSuiteTests[$this->testSuiteLevel]++;
$this->testSuiteTimes[$this->testSuiteLevel] += $time;
$testOutput = '';
if (\method_exists($test, 'hasOutput') && \method_exists($test, 'getActualOutput')) {
$testOutput = $test->hasOutput() ? $test->getActualOutput() : '';
}
if (!empty($testOutput)) {
$systemOut = $this->document->createElement(
'system-out',
Xml::prepareString($testOutput)
);
$this->currentTestCase->appendChild($systemOut);
}
$this->currentTestCase = null;
}
/**
* Returns the XML as a string.
*/
public function getXML(): string
{
return $this->document->saveXML();
}
private function doAddFault(Test $test, \Throwable $t, float $time, $type): void
{
if ($this->currentTestCase === null) {
return;
}
if ($test instanceof SelfDescribing) {
$buffer = $test->toString() . "\n";
} else {
$buffer = '';
}
$buffer .= TestFailure::exceptionToString($t) . "\n" .
Filter::getFilteredStacktrace($t);
$fault = $this->document->createElement(
$type,
Xml::prepareString($buffer)
);
if ($t instanceof ExceptionWrapper) {
$fault->setAttribute('type', $t->getClassName());
} else {
$fault->setAttribute('type', \get_class($t));
}
$this->currentTestCase->appendChild($fault);
}
private function doAddSkipped(): void
{
if ($this->currentTestCase === null) {
return;
}
$skipped = $this->document->createElement('skipped');
$this->currentTestCase->appendChild($skipped);
$this->testSuiteSkipped[$this->testSuiteLevel]++;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\Log;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExceptionWrapper;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\TextUI\ResultPrinter;
use PHPUnit\Util\Exception;
use PHPUnit\Util\Filter;
use ReflectionClass;
use SebastianBergmann\Comparator\ComparisonFailure;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TeamCity extends ResultPrinter
{
/**
* @var bool
*/
private $isSummaryTestCountPrinted = false;
/**
* @var string
*/
private $startedTestName;
/**
* @var false|int
*/
private $flowId;
/**
* @throws \SebastianBergmann\Timer\RuntimeException
*/
public function printResult(TestResult $result): void
{
$this->printHeader();
$this->printFooter($result);
}
/**
* An error occurred.
*/
public function addError(Test $test, \Throwable $t, float $time): void
{
$this->printEvent(
'testFailed',
[
'name' => $test->getName(),
'message' => self::getMessage($t),
'details' => self::getDetails($t),
'duration' => self::toMilliseconds($time),
]
);
}
/**
* A warning occurred.
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
$this->printEvent(
'testFailed',
[
'name' => $test->getName(),
'message' => self::getMessage($e),
'details' => self::getDetails($e),
'duration' => self::toMilliseconds($time),
]
);
}
/**
* A failure occurred.
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$parameters = [
'name' => $test->getName(),
'message' => self::getMessage($e),
'details' => self::getDetails($e),
'duration' => self::toMilliseconds($time),
];
if ($e instanceof ExpectationFailedException) {
$comparisonFailure = $e->getComparisonFailure();
if ($comparisonFailure instanceof ComparisonFailure) {
$expectedString = $comparisonFailure->getExpectedAsString();
if ($expectedString === null || empty($expectedString)) {
$expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected());
}
$actualString = $comparisonFailure->getActualAsString();
if ($actualString === null || empty($actualString)) {
$actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual());
}
if ($actualString !== null && $expectedString !== null) {
$parameters['type'] = 'comparisonFailure';
$parameters['actual'] = $actualString;
$parameters['expected'] = $expectedString;
}
}
}
$this->printEvent('testFailed', $parameters);
}
/**
* Incomplete test.
*/
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
{
$this->printIgnoredTest($test->getName(), $t, $time);
}
/**
* Risky test.
*/
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
{
$this->addError($test, $t, $time);
}
/**
* Skipped test.
*/
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
{
$testName = $test->getName();
if ($this->startedTestName !== $testName) {
$this->startTest($test);
$this->printIgnoredTest($testName, $t, $time);
$this->endTest($test, $time);
} else {
$this->printIgnoredTest($testName, $t, $time);
}
}
public function printIgnoredTest($testName, \Throwable $t, float $time): void
{
$this->printEvent(
'testIgnored',
[
'name' => $testName,
'message' => self::getMessage($t),
'details' => self::getDetails($t),
'duration' => self::toMilliseconds($time),
]
);
}
/**
* A testsuite started.
*/
public function startTestSuite(TestSuite $suite): void
{
if (\stripos(\ini_get('disable_functions'), 'getmypid') === false) {
$this->flowId = \getmypid();
} else {
$this->flowId = false;
}
if (!$this->isSummaryTestCountPrinted) {
$this->isSummaryTestCountPrinted = true;
$this->printEvent(
'testCount',
['count' => \count($suite)]
);
}
$suiteName = $suite->getName();
if (empty($suiteName)) {
return;
}
$parameters = ['name' => $suiteName];
if (\class_exists($suiteName, false)) {
$fileName = self::getFileName($suiteName);
$parameters['locationHint'] = "php_qn://$fileName::\\$suiteName";
} else {
$split = \explode('::', $suiteName);
if (\count($split) === 2 && \class_exists($split[0]) && \method_exists($split[0], $split[1])) {
$fileName = self::getFileName($split[0]);
$parameters['locationHint'] = "php_qn://$fileName::\\$suiteName";
$parameters['name'] = $split[1];
}
}
$this->printEvent('testSuiteStarted', $parameters);
}
/**
* A testsuite ended.
*/
public function endTestSuite(TestSuite $suite): void
{
$suiteName = $suite->getName();
if (empty($suiteName)) {
return;
}
$parameters = ['name' => $suiteName];
if (!\class_exists($suiteName, false)) {
$split = \explode('::', $suiteName);
if (\count($split) === 2 && \class_exists($split[0]) && \method_exists($split[0], $split[1])) {
$parameters['name'] = $split[1];
}
}
$this->printEvent('testSuiteFinished', $parameters);
}
/**
* A test started.
*/
public function startTest(Test $test): void
{
$testName = $test->getName();
$this->startedTestName = $testName;
$params = ['name' => $testName];
if ($test instanceof TestCase) {
$className = \get_class($test);
$fileName = self::getFileName($className);
$params['locationHint'] = "php_qn://$fileName::\\$className::$testName";
}
$this->printEvent('testStarted', $params);
}
/**
* A test ended.
*/
public function endTest(Test $test, float $time): void
{
parent::endTest($test, $time);
$this->printEvent(
'testFinished',
[
'name' => $test->getName(),
'duration' => self::toMilliseconds($time),
]
);
}
protected function writeProgress(string $progress): void
{
}
private function printEvent(string $eventName, array $params = []): void
{
$this->write("\n##teamcity[$eventName");
if ($this->flowId) {
$params['flowId'] = $this->flowId;
}
foreach ($params as $key => $value) {
$escapedValue = self::escapeValue((string) $value);
$this->write(" $key='$escapedValue'");
}
$this->write("]\n");
}
private static function getMessage(\Throwable $t): string
{
$message = '';
if ($t instanceof ExceptionWrapper) {
if ($t->getClassName() !== '') {
$message .= $t->getClassName();
}
if ($message !== '' && $t->getMessage() !== '') {
$message .= ' : ';
}
}
return $message . $t->getMessage();
}
private static function getDetails(\Throwable $t): string
{
$stackTrace = Filter::getFilteredStacktrace($t);
$previous = $t instanceof ExceptionWrapper ? $t->getPreviousWrapped() : $t->getPrevious();
while ($previous) {
$stackTrace .= "\nCaused by\n" .
TestFailure::exceptionToString($previous) . "\n" .
Filter::getFilteredStacktrace($previous);
$previous = $previous instanceof ExceptionWrapper ?
$previous->getPreviousWrapped() : $previous->getPrevious();
}
return ' ' . \str_replace("\n", "\n ", $stackTrace);
}
private static function getPrimitiveValueAsString($value): ?string
{
if ($value === null) {
return 'null';
}
if (\is_bool($value)) {
return $value ? 'true' : 'false';
}
if (\is_scalar($value)) {
return \print_r($value, true);
}
return null;
}
private static function escapeValue(string $text): string
{
return \str_replace(
['|', "'", "\n", "\r", ']', '['],
['||', "|'", '|n', '|r', '|]', '|['],
$text
);
}
/**
* @param string $className
*/
private static function getFileName($className): string
{
try {
return (new ReflectionClass($className))->getFileName();
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
}
/**
* @param float $time microseconds
*/
private static function toMilliseconds(float $time): int
{
return (int) \round($time * 1000);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Json
{
/**
* Prettify json string
*
* @throws \PHPUnit\Framework\Exception
*/
public static function prettify(string $json): string
{
$decodedJson = \json_decode($json, true);
if (\json_last_error()) {
throw new Exception(
'Cannot prettify invalid json'
);
}
return \json_encode($decodedJson, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES);
}
/*
* To allow comparison of JSON strings, first process them into a consistent
* format so that they can be compared as strings.
* @return array ($error, $canonicalized_json) The $error parameter is used
* to indicate an error decoding the json. This is used to avoid ambiguity
* with JSON strings consisting entirely of 'null' or 'false'.
*/
public static function canonicalize(string $json): array
{
$decodedJson = \json_decode($json);
if (\json_last_error()) {
return [true, null];
}
self::recursiveSort($decodedJson);
$reencodedJson = \json_encode($decodedJson);
return [false, $reencodedJson];
}
/*
* JSON object keys are unordered while PHP array keys are ordered.
* Sort all array keys to ensure both the expected and actual values have
* their keys in the same order.
*/
private static function recursiveSort(&$json): void
{
if (!\is_array($json)) {
// If the object is not empty, change it to an associative array
// so we can sort the keys (and we will still re-encode it
// correctly, since PHP encodes associative arrays as JSON objects.)
// But EMPTY objects MUST remain empty objects. (Otherwise we will
// re-encode it as a JSON array rather than a JSON object.)
// See #2919.
if (\is_object($json) && \count((array) $json) > 0) {
$json = (array) $json;
} else {
return;
}
}
\ksort($json);
foreach ($json as $key => &$value) {
self::recursiveSort($value);
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Exception extends \RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\PHP;
use __PHP_Incomplete_Class;
use ErrorException;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\SyntheticError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestResult;
use SebastianBergmann\Environment\Runtime;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
abstract class AbstractPhpProcess
{
/**
* @var Runtime
*/
protected $runtime;
/**
* @var bool
*/
protected $stderrRedirection = false;
/**
* @var string
*/
protected $stdin = '';
/**
* @var string
*/
protected $args = '';
/**
* @var array<string, string>
*/
protected $env = [];
/**
* @var int
*/
protected $timeout = 0;
public static function factory(): self
{
if (\DIRECTORY_SEPARATOR === '\\') {
return new WindowsPhpProcess;
}
return new DefaultPhpProcess;
}
public function __construct()
{
$this->runtime = new Runtime;
}
/**
* Defines if should use STDERR redirection or not.
*
* Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT.
*/
public function setUseStderrRedirection(bool $stderrRedirection): void
{
$this->stderrRedirection = $stderrRedirection;
}
/**
* Returns TRUE if uses STDERR redirection or FALSE if not.
*/
public function useStderrRedirection(): bool
{
return $this->stderrRedirection;
}
/**
* Sets the input string to be sent via STDIN
*/
public function setStdin(string $stdin): void
{
$this->stdin = $stdin;
}
/**
* Returns the input string to be sent via STDIN
*/
public function getStdin(): string
{
return $this->stdin;
}
/**
* Sets the string of arguments to pass to the php job
*/
public function setArgs(string $args): void
{
$this->args = $args;
}
/**
* Returns the string of arguments to pass to the php job
*/
public function getArgs(): string
{
return $this->args;
}
/**
* Sets the array of environment variables to start the child process with
*
* @param array<string, string> $env
*/
public function setEnv(array $env): void
{
$this->env = $env;
}
/**
* Returns the array of environment variables to start the child process with
*/
public function getEnv(): array
{
return $this->env;
}
/**
* Sets the amount of seconds to wait before timing out
*/
public function setTimeout(int $timeout): void
{
$this->timeout = $timeout;
}
/**
* Returns the amount of seconds to wait before timing out
*/
public function getTimeout(): int
{
return $this->timeout;
}
/**
* Runs a single test in a separate PHP process.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function runTestJob(string $job, Test $test, TestResult $result): void
{
$result->startTest($test);
$_result = $this->runJob($job);
$this->processChildResult(
$test,
$result,
$_result['stdout'],
$_result['stderr']
);
}
/**
* Returns the command based into the configurations.
*/
public function getCommand(array $settings, string $file = null): string
{
$command = $this->runtime->getBinary();
if ($this->runtime->hasPCOV()) {
$settings = \array_merge(
$settings,
$this->runtime->getCurrentSettings(
\array_keys(\ini_get_all('pcov'))
)
);
} elseif ($this->runtime->hasXdebug()) {
$settings = \array_merge(
$settings,
$this->runtime->getCurrentSettings(
\array_keys(\ini_get_all('xdebug'))
)
);
}
$command .= $this->settingsToParameters($settings);
if (\PHP_SAPI === 'phpdbg') {
$command .= ' -qrr';
if (!$file) {
$command .= 's=';
}
}
if ($file) {
$command .= ' ' . \escapeshellarg($file);
}
if ($this->args) {
if (!$file) {
$command .= ' --';
}
$command .= ' ' . $this->args;
}
if ($this->stderrRedirection) {
$command .= ' 2>&1';
}
return $command;
}
/**
* Runs a single job (PHP code) using a separate PHP process.
*/
abstract public function runJob(string $job, array $settings = []): array;
protected function settingsToParameters(array $settings): string
{
$buffer = '';
foreach ($settings as $setting) {
$buffer .= ' -d ' . \escapeshellarg($setting);
}
return $buffer;
}
/**
* Processes the TestResult object from an isolated process.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function processChildResult(Test $test, TestResult $result, string $stdout, string $stderr): void
{
$time = 0;
if (!empty($stderr)) {
$result->addError(
$test,
new Exception(\trim($stderr)),
$time
);
} else {
\set_error_handler(
/**
* @throws ErrorException
*/
static function ($errno, $errstr, $errfile, $errline): void {
throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
}
);
try {
if (\strpos($stdout, "#!/usr/bin/env php\n") === 0) {
$stdout = \substr($stdout, 19);
}
$childResult = \unserialize(\str_replace("#!/usr/bin/env php\n", '', $stdout));
\restore_error_handler();
} catch (ErrorException $e) {
\restore_error_handler();
$childResult = false;
$result->addError(
$test,
new Exception(\trim($stdout), 0, $e),
$time
);
}
if ($childResult !== false) {
if (!empty($childResult['output'])) {
$output = $childResult['output'];
}
/* @var TestCase $test */
$test->setResult($childResult['testResult']);
$test->addToAssertionCount($childResult['numAssertions']);
$childResult = $childResult['result'];
\assert($childResult instanceof TestResult);
if ($result->getCollectCodeCoverageInformation()) {
$result->getCodeCoverage()->merge(
$childResult->getCodeCoverage()
);
}
$time = $childResult->time();
$notImplemented = $childResult->notImplemented();
$risky = $childResult->risky();
$skipped = $childResult->skipped();
$errors = $childResult->errors();
$warnings = $childResult->warnings();
$failures = $childResult->failures();
if (!empty($notImplemented)) {
$result->addError(
$test,
$this->getException($notImplemented[0]),
$time
);
} elseif (!empty($risky)) {
$result->addError(
$test,
$this->getException($risky[0]),
$time
);
} elseif (!empty($skipped)) {
$result->addError(
$test,
$this->getException($skipped[0]),
$time
);
} elseif (!empty($errors)) {
$result->addError(
$test,
$this->getException($errors[0]),
$time
);
} elseif (!empty($warnings)) {
$result->addWarning(
$test,
$this->getException($warnings[0]),
$time
);
} elseif (!empty($failures)) {
$result->addFailure(
$test,
$this->getException($failures[0]),
$time
);
}
}
}
$result->endTest($test, $time);
if (!empty($output)) {
print $output;
}
}
/**
* Gets the thrown exception from a PHPUnit\Framework\TestFailure.
*
* @see https://github.com/sebastianbergmann/phpunit/issues/74
*/
private function getException(TestFailure $error): Exception
{
$exception = $error->thrownException();
if ($exception instanceof __PHP_Incomplete_Class) {
$exceptionArray = [];
foreach ((array) $exception as $key => $value) {
$key = \substr($key, \strrpos($key, "\0") + 1);
$exceptionArray[$key] = $value;
}
$exception = new SyntheticError(
\sprintf(
'%s: %s',
$exceptionArray['_PHP_Incomplete_Class_Name'],
$exceptionArray['message']
),
$exceptionArray['code'],
$exceptionArray['file'],
$exceptionArray['line'],
$exceptionArray['trace']
);
}
return $exception;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\PHP;
use PHPUnit\Framework\Exception;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*
* @see https://bugs.php.net/bug.php?id=51800
*/
final class WindowsPhpProcess extends DefaultPhpProcess
{
public function getCommand(array $settings, string $file = null): string
{
return '"' . parent::getCommand($settings, $file) . '"';
}
/**
* @throws Exception
*/
protected function getHandles(): array
{
if (false === $stdout_handle = \tmpfile()) {
throw new Exception(
'A temporary file could not be created; verify that your TEMP environment variable is writable'
);
}
return [
1 => $stdout_handle,
];
}
protected function useTemporaryFile(): bool
{
return true;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
eval('?>' . \file_get_contents('php://stdin'));
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util\PHP;
use PHPUnit\Framework\Exception;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class DefaultPhpProcess extends AbstractPhpProcess
{
/**
* @var string
*/
protected $tempFile;
/**
* Runs a single job (PHP code) using a separate PHP process.
*
* @throws Exception
*/
public function runJob(string $job, array $settings = []): array
{
if ($this->stdin || $this->useTemporaryFile()) {
if (!($this->tempFile = \tempnam(\sys_get_temp_dir(), 'PHPUnit')) ||
\file_put_contents($this->tempFile, $job) === false) {
throw new Exception(
'Unable to write temporary file'
);
}
$job = $this->stdin;
}
return $this->runProcess($job, $settings);
}
/**
* Returns an array of file handles to be used in place of pipes
*/
protected function getHandles(): array
{
return [];
}
/**
* Handles creating the child process and returning the STDOUT and STDERR
*
* @throws Exception
*/
protected function runProcess(string $job, array $settings): array
{
$handles = $this->getHandles();
$env = null;
if ($this->env) {
$env = $_SERVER ?? [];
unset($env['argv'], $env['argc']);
$env = \array_merge($env, $this->env);
foreach ($env as $envKey => $envVar) {
if (\is_array($envVar)) {
unset($env[$envKey]);
}
}
}
$pipeSpec = [
0 => $handles[0] ?? ['pipe', 'r'],
1 => $handles[1] ?? ['pipe', 'w'],
2 => $handles[2] ?? ['pipe', 'w'],
];
$process = \proc_open(
$this->getCommand($settings, $this->tempFile),
$pipeSpec,
$pipes,
null,
$env
);
if (!\is_resource($process)) {
throw new Exception(
'Unable to spawn worker process'
);
}
if ($job) {
$this->process($pipes[0], $job);
}
\fclose($pipes[0]);
$stderr = $stdout = '';
if ($this->timeout) {
unset($pipes[0]);
while (true) {
$r = $pipes;
$w = null;
$e = null;
$n = @\stream_select($r, $w, $e, $this->timeout);
if ($n === false) {
break;
}
if ($n === 0) {
\proc_terminate($process, 9);
throw new Exception(
\sprintf(
'Job execution aborted after %d seconds',
$this->timeout
)
);
}
if ($n > 0) {
foreach ($r as $pipe) {
$pipeOffset = 0;
foreach ($pipes as $i => $origPipe) {
if ($pipe === $origPipe) {
$pipeOffset = $i;
break;
}
}
if (!$pipeOffset) {
break;
}
$line = \fread($pipe, 8192);
if ($line === '') {
\fclose($pipes[$pipeOffset]);
unset($pipes[$pipeOffset]);
} elseif ($pipeOffset === 1) {
$stdout .= $line;
} else {
$stderr .= $line;
}
}
if (empty($pipes)) {
break;
}
}
}
} else {
if (isset($pipes[1])) {
$stdout = \stream_get_contents($pipes[1]);
\fclose($pipes[1]);
}
if (isset($pipes[2])) {
$stderr = \stream_get_contents($pipes[2]);
\fclose($pipes[2]);
}
}
if (isset($handles[1])) {
\rewind($handles[1]);
$stdout = \stream_get_contents($handles[1]);
\fclose($handles[1]);
}
if (isset($handles[2])) {
\rewind($handles[2]);
$stderr = \stream_get_contents($handles[2]);
\fclose($handles[2]);
}
\proc_close($process);
$this->cleanup();
return ['stdout' => $stdout, 'stderr' => $stderr];
}
protected function process($pipe, string $job): void
{
\fwrite($pipe, $job);
}
protected function cleanup(): void
{
if ($this->tempFile) {
\unlink($this->tempFile);
}
}
protected function useTemporaryFile(): bool
{
return false;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use DOMElement;
use DOMXPath;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\TestSuiteSorter;
use PHPUnit\TextUI\ResultPrinter;
use PHPUnit\Util\TestDox\CliTestDoxPrinter;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Configuration
{
/**
* @var self[]
*/
private static $instances = [];
/**
* @var \DOMDocument
*/
private $document;
/**
* @var DOMXPath
*/
private $xpath;
/**
* @var string
*/
private $filename;
/**
* @var \LibXMLError[]
*/
private $errors = [];
/**
* Returns a PHPUnit configuration object.
*
* @throws Exception
*/
public static function getInstance(string $filename): self
{
$realPath = \realpath($filename);
if ($realPath === false) {
throw new Exception(
\sprintf(
'Could not read "%s".',
$filename
)
);
}
if (!isset(self::$instances[$realPath])) {
self::$instances[$realPath] = new self($realPath);
}
return self::$instances[$realPath];
}
/**
* Loads a PHPUnit configuration file.
*
* @throws Exception
*/
private function __construct(string $filename)
{
$this->filename = $filename;
$this->document = Xml::loadFile($filename, false, true, true);
$this->xpath = new DOMXPath($this->document);
$this->validateConfigurationAgainstSchema();
}
/**
* @codeCoverageIgnore
*/
private function __clone()
{
}
public function hasValidationErrors(): bool
{
return \count($this->errors) > 0;
}
public function getValidationErrors(): array
{
$result = [];
foreach ($this->errors as $error) {
if (!isset($result[$error->line])) {
$result[$error->line] = [];
}
$result[$error->line][] = \trim($error->message);
}
return $result;
}
/**
* Returns the real path to the configuration file.
*/
public function getFilename(): string
{
return $this->filename;
}
public function getExtensionConfiguration(): array
{
$result = [];
foreach ($this->xpath->query('extensions/extension') as $extension) {
$result[] = $this->getElementConfigurationParameters($extension);
}
return $result;
}
/**
* Returns the configuration for SUT filtering.
*/
public function getFilterConfiguration(): array
{
$addUncoveredFilesFromWhitelist = true;
$processUncoveredFilesFromWhitelist = false;
$includeDirectory = [];
$includeFile = [];
$excludeDirectory = [];
$excludeFile = [];
$tmp = $this->xpath->query('filter/whitelist');
if ($tmp->length === 1) {
if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) {
$addUncoveredFilesFromWhitelist = $this->getBoolean(
(string) $tmp->item(0)->getAttribute(
'addUncoveredFilesFromWhitelist'
),
true
);
}
if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) {
$processUncoveredFilesFromWhitelist = $this->getBoolean(
(string) $tmp->item(0)->getAttribute(
'processUncoveredFilesFromWhitelist'
),
false
);
}
$includeDirectory = $this->readFilterDirectories(
'filter/whitelist/directory'
);
$includeFile = $this->readFilterFiles(
'filter/whitelist/file'
);
$excludeDirectory = $this->readFilterDirectories(
'filter/whitelist/exclude/directory'
);
$excludeFile = $this->readFilterFiles(
'filter/whitelist/exclude/file'
);
}
return [
'whitelist' => [
'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist,
'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist,
'include' => [
'directory' => $includeDirectory,
'file' => $includeFile,
],
'exclude' => [
'directory' => $excludeDirectory,
'file' => $excludeFile,
],
],
];
}
/**
* Returns the configuration for groups.
*/
public function getGroupConfiguration(): array
{
return $this->parseGroupConfiguration('groups');
}
/**
* Returns the configuration for testdox groups.
*/
public function getTestdoxGroupConfiguration(): array
{
return $this->parseGroupConfiguration('testdoxGroups');
}
/**
* Returns the configuration for listeners.
*/
public function getListenerConfiguration(): array
{
$result = [];
foreach ($this->xpath->query('listeners/listener') as $listener) {
$result[] = $this->getElementConfigurationParameters($listener);
}
return $result;
}
/**
* Returns the logging configuration.
*/
public function getLoggingConfiguration(): array
{
$result = [];
foreach ($this->xpath->query('logging/log') as $log) {
\assert($log instanceof DOMElement);
$type = (string) $log->getAttribute('type');
$target = (string) $log->getAttribute('target');
if (!$target) {
continue;
}
$target = $this->toAbsolutePath($target);
if ($type === 'coverage-html') {
if ($log->hasAttribute('lowUpperBound')) {
$result['lowUpperBound'] = $this->getInteger(
(string) $log->getAttribute('lowUpperBound'),
50
);
}
if ($log->hasAttribute('highLowerBound')) {
$result['highLowerBound'] = $this->getInteger(
(string) $log->getAttribute('highLowerBound'),
90
);
}
} elseif ($type === 'coverage-crap4j') {
if ($log->hasAttribute('threshold')) {
$result['crap4jThreshold'] = $this->getInteger(
(string) $log->getAttribute('threshold'),
30
);
}
} elseif ($type === 'coverage-text') {
if ($log->hasAttribute('showUncoveredFiles')) {
$result['coverageTextShowUncoveredFiles'] = $this->getBoolean(
(string) $log->getAttribute('showUncoveredFiles'),
false
);
}
if ($log->hasAttribute('showOnlySummary')) {
$result['coverageTextShowOnlySummary'] = $this->getBoolean(
(string) $log->getAttribute('showOnlySummary'),
false
);
}
}
$result[$type] = $target;
}
return $result;
}
/**
* Returns the PHP configuration.
*/
public function getPHPConfiguration(): array
{
$result = [
'include_path' => [],
'ini' => [],
'const' => [],
'var' => [],
'env' => [],
'post' => [],
'get' => [],
'cookie' => [],
'server' => [],
'files' => [],
'request' => [],
];
foreach ($this->xpath->query('php/includePath') as $includePath) {
$path = (string) $includePath->textContent;
if ($path) {
$result['include_path'][] = $this->toAbsolutePath($path);
}
}
foreach ($this->xpath->query('php/ini') as $ini) {
\assert($ini instanceof DOMElement);
$name = (string) $ini->getAttribute('name');
$value = (string) $ini->getAttribute('value');
$result['ini'][$name]['value'] = $value;
}
foreach ($this->xpath->query('php/const') as $const) {
\assert($const instanceof DOMElement);
$name = (string) $const->getAttribute('name');
$value = (string) $const->getAttribute('value');
$result['const'][$name]['value'] = $this->getBoolean($value, $value);
}
foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
foreach ($this->xpath->query('php/' . $array) as $var) {
\assert($var instanceof DOMElement);
$name = (string) $var->getAttribute('name');
$value = (string) $var->getAttribute('value');
$verbatim = false;
if ($var->hasAttribute('verbatim')) {
$verbatim = $this->getBoolean($var->getAttribute('verbatim'), false);
$result[$array][$name]['verbatim'] = $verbatim;
}
if ($var->hasAttribute('force')) {
$force = $this->getBoolean($var->getAttribute('force'), false);
$result[$array][$name]['force'] = $force;
}
if (!$verbatim) {
$value = $this->getBoolean($value, $value);
}
$result[$array][$name]['value'] = $value;
}
}
return $result;
}
/**
* Handles the PHP configuration.
*/
public function handlePHPConfiguration(): void
{
$configuration = $this->getPHPConfiguration();
if (!empty($configuration['include_path'])) {
\ini_set(
'include_path',
\implode(\PATH_SEPARATOR, $configuration['include_path']) .
\PATH_SEPARATOR .
\ini_get('include_path')
);
}
foreach ($configuration['ini'] as $name => $data) {
$value = $data['value'];
if (\defined($value)) {
$value = (string) \constant($value);
}
\ini_set($name, $value);
}
foreach ($configuration['const'] as $name => $data) {
$value = $data['value'];
if (!\defined($name)) {
\define($name, $value);
}
}
foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
/*
* @see https://github.com/sebastianbergmann/phpunit/issues/277
*/
switch ($array) {
case 'var':
$target = &$GLOBALS;
break;
case 'server':
$target = &$_SERVER;
break;
default:
$target = &$GLOBALS['_' . \strtoupper($array)];
break;
}
foreach ($configuration[$array] as $name => $data) {
$target[$name] = $data['value'];
}
}
foreach ($configuration['env'] as $name => $data) {
$value = $data['value'];
$force = $data['force'] ?? false;
if ($force || \getenv($name) === false) {
\putenv("{$name}={$value}");
}
$value = \getenv($name);
if (!isset($_ENV[$name])) {
$_ENV[$name] = $value;
}
if ($force) {
$_ENV[$name] = $value;
}
}
}
/**
* Returns the PHPUnit configuration.
*/
public function getPHPUnitConfiguration(): array
{
$result = [];
$root = $this->document->documentElement;
if ($root->hasAttribute('cacheTokens')) {
$result['cacheTokens'] = $this->getBoolean(
(string) $root->getAttribute('cacheTokens'),
false
);
}
if ($root->hasAttribute('columns')) {
$columns = (string) $root->getAttribute('columns');
if ($columns === 'max') {
$result['columns'] = 'max';
} else {
$result['columns'] = $this->getInteger($columns, 80);
}
}
if ($root->hasAttribute('colors')) {
/* only allow boolean for compatibility with previous versions
'always' only allowed from command line */
if ($this->getBoolean($root->getAttribute('colors'), false)) {
$result['colors'] = ResultPrinter::COLOR_AUTO;
} else {
$result['colors'] = ResultPrinter::COLOR_NEVER;
}
}
/*
* @see https://github.com/sebastianbergmann/phpunit/issues/657
*/
if ($root->hasAttribute('stderr')) {
$result['stderr'] = $this->getBoolean(
(string) $root->getAttribute('stderr'),
false
);
}
if ($root->hasAttribute('backupGlobals')) {
$result['backupGlobals'] = $this->getBoolean(
(string) $root->getAttribute('backupGlobals'),
false
);
}
if ($root->hasAttribute('backupStaticAttributes')) {
$result['backupStaticAttributes'] = $this->getBoolean(
(string) $root->getAttribute('backupStaticAttributes'),
false
);
}
if ($root->getAttribute('bootstrap')) {
$result['bootstrap'] = $this->toAbsolutePath(
(string) $root->getAttribute('bootstrap')
);
}
if ($root->hasAttribute('convertDeprecationsToExceptions')) {
$result['convertDeprecationsToExceptions'] = $this->getBoolean(
(string) $root->getAttribute('convertDeprecationsToExceptions'),
true
);
}
if ($root->hasAttribute('convertErrorsToExceptions')) {
$result['convertErrorsToExceptions'] = $this->getBoolean(
(string) $root->getAttribute('convertErrorsToExceptions'),
true
);
}
if ($root->hasAttribute('convertNoticesToExceptions')) {
$result['convertNoticesToExceptions'] = $this->getBoolean(
(string) $root->getAttribute('convertNoticesToExceptions'),
true
);
}
if ($root->hasAttribute('convertWarningsToExceptions')) {
$result['convertWarningsToExceptions'] = $this->getBoolean(
(string) $root->getAttribute('convertWarningsToExceptions'),
true
);
}
if ($root->hasAttribute('forceCoversAnnotation')) {
$result['forceCoversAnnotation'] = $this->getBoolean(
(string) $root->getAttribute('forceCoversAnnotation'),
false
);
}
if ($root->hasAttribute('disableCodeCoverageIgnore')) {
$result['disableCodeCoverageIgnore'] = $this->getBoolean(
(string) $root->getAttribute('disableCodeCoverageIgnore'),
false
);
}
if ($root->hasAttribute('processIsolation')) {
$result['processIsolation'] = $this->getBoolean(
(string) $root->getAttribute('processIsolation'),
false
);
}
if ($root->hasAttribute('stopOnDefect')) {
$result['stopOnDefect'] = $this->getBoolean(
(string) $root->getAttribute('stopOnDefect'),
false
);
}
if ($root->hasAttribute('stopOnError')) {
$result['stopOnError'] = $this->getBoolean(
(string) $root->getAttribute('stopOnError'),
false
);
}
if ($root->hasAttribute('stopOnFailure')) {
$result['stopOnFailure'] = $this->getBoolean(
(string) $root->getAttribute('stopOnFailure'),
false
);
}
if ($root->hasAttribute('stopOnWarning')) {
$result['stopOnWarning'] = $this->getBoolean(
(string) $root->getAttribute('stopOnWarning'),
false
);
}
if ($root->hasAttribute('stopOnIncomplete')) {
$result['stopOnIncomplete'] = $this->getBoolean(
(string) $root->getAttribute('stopOnIncomplete'),
false
);
}
if ($root->hasAttribute('stopOnRisky')) {
$result['stopOnRisky'] = $this->getBoolean(
(string) $root->getAttribute('stopOnRisky'),
false
);
}
if ($root->hasAttribute('stopOnSkipped')) {
$result['stopOnSkipped'] = $this->getBoolean(
(string) $root->getAttribute('stopOnSkipped'),
false
);
}
if ($root->hasAttribute('failOnWarning')) {
$result['failOnWarning'] = $this->getBoolean(
(string) $root->getAttribute('failOnWarning'),
false
);
}
if ($root->hasAttribute('failOnRisky')) {
$result['failOnRisky'] = $this->getBoolean(
(string) $root->getAttribute('failOnRisky'),
false
);
}
if ($root->hasAttribute('testSuiteLoaderClass')) {
$result['testSuiteLoaderClass'] = (string) $root->getAttribute(
'testSuiteLoaderClass'
);
}
if ($root->hasAttribute('defaultTestSuite')) {
$result['defaultTestSuite'] = (string) $root->getAttribute(
'defaultTestSuite'
);
}
if ($root->getAttribute('testSuiteLoaderFile')) {
$result['testSuiteLoaderFile'] = $this->toAbsolutePath(
(string) $root->getAttribute('testSuiteLoaderFile')
);
}
if ($root->hasAttribute('printerClass')) {
$result['printerClass'] = (string) $root->getAttribute(
'printerClass'
);
}
if ($root->getAttribute('printerFile')) {
$result['printerFile'] = $this->toAbsolutePath(
(string) $root->getAttribute('printerFile')
);
}
if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) {
$result['beStrictAboutChangesToGlobalState'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutChangesToGlobalState'),
false
);
}
if ($root->hasAttribute('beStrictAboutOutputDuringTests')) {
$result['disallowTestOutput'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutOutputDuringTests'),
false
);
}
if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) {
$result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'),
false
);
}
if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) {
$result['reportUselessTests'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'),
true
);
}
if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) {
$result['disallowTodoAnnotatedTests'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'),
false
);
}
if ($root->hasAttribute('beStrictAboutCoversAnnotation')) {
$result['strictCoverage'] = $this->getBoolean(
(string) $root->getAttribute('beStrictAboutCoversAnnotation'),
false
);
}
if ($root->hasAttribute('defaultTimeLimit')) {
$result['defaultTimeLimit'] = $this->getInteger(
(string) $root->getAttribute('defaultTimeLimit'),
1
);
}
if ($root->hasAttribute('enforceTimeLimit')) {
$result['enforceTimeLimit'] = $this->getBoolean(
(string) $root->getAttribute('enforceTimeLimit'),
false
);
}
if ($root->hasAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage')) {
$result['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $this->getBoolean(
(string) $root->getAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage'),
false
);
}
if ($root->hasAttribute('timeoutForSmallTests')) {
$result['timeoutForSmallTests'] = $this->getInteger(
(string) $root->getAttribute('timeoutForSmallTests'),
1
);
}
if ($root->hasAttribute('timeoutForMediumTests')) {
$result['timeoutForMediumTests'] = $this->getInteger(
(string) $root->getAttribute('timeoutForMediumTests'),
10
);
}
if ($root->hasAttribute('timeoutForLargeTests')) {
$result['timeoutForLargeTests'] = $this->getInteger(
(string) $root->getAttribute('timeoutForLargeTests'),
60
);
}
if ($root->hasAttribute('reverseDefectList')) {
$result['reverseDefectList'] = $this->getBoolean(
(string) $root->getAttribute('reverseDefectList'),
false
);
}
if ($root->hasAttribute('verbose')) {
$result['verbose'] = $this->getBoolean(
(string) $root->getAttribute('verbose'),
false
);
}
if ($root->hasAttribute('testdox')) {
$testdox = $this->getBoolean(
(string) $root->getAttribute('testdox'),
false
);
if ($testdox) {
if (isset($result['printerClass'])) {
$result['conflictBetweenPrinterClassAndTestdox'] = true;
} else {
$result['printerClass'] = CliTestDoxPrinter::class;
}
}
}
if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) {
$result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean(
(string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'),
false
);
}
if ($root->hasAttribute('extensionsDirectory')) {
$result['extensionsDirectory'] = $this->toAbsolutePath(
(string) $root->getAttribute(
'extensionsDirectory'
)
);
}
if ($root->hasAttribute('cacheResult')) {
$result['cacheResult'] = $this->getBoolean(
(string) $root->getAttribute('cacheResult'),
true
);
}
if ($root->hasAttribute('cacheResultFile')) {
$result['cacheResultFile'] = $this->toAbsolutePath(
(string) $root->getAttribute('cacheResultFile')
);
}
if ($root->hasAttribute('executionOrder')) {
foreach (\explode(',', $root->getAttribute('executionOrder')) as $order) {
switch ($order) {
case 'default':
$result['executionOrder'] = TestSuiteSorter::ORDER_DEFAULT;
$result['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFAULT;
$result['resolveDependencies'] = false;
break;
case 'defects':
$result['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFECTS_FIRST;
break;
case 'depends':
$result['resolveDependencies'] = true;
break;
case 'duration':
$result['executionOrder'] = TestSuiteSorter::ORDER_DURATION;
break;
case 'no-depends':
$result['resolveDependencies'] = false;
break;
case 'random':
$result['executionOrder'] = TestSuiteSorter::ORDER_RANDOMIZED;
break;
case 'reverse':
$result['executionOrder'] = TestSuiteSorter::ORDER_REVERSED;
break;
case 'size':
$result['executionOrder'] = TestSuiteSorter::ORDER_SIZE;
break;
}
}
}
if ($root->hasAttribute('resolveDependencies')) {
$result['resolveDependencies'] = $this->getBoolean(
(string) $root->getAttribute('resolveDependencies'),
false
);
}
if ($root->hasAttribute('noInteraction')) {
$result['noInteraction'] = $this->getBoolean(
(string) $root->getAttribute('noInteraction'),
false
);
}
return $result;
}
/**
* Returns the test suite configuration.
*
* @throws Exception
*/
public function getTestSuiteConfiguration(string $testSuiteFilter = ''): TestSuite
{
$testSuiteNodes = $this->xpath->query('testsuites/testsuite');
if ($testSuiteNodes->length === 0) {
$testSuiteNodes = $this->xpath->query('testsuite');
}
if ($testSuiteNodes->length === 1) {
return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter);
}
$suite = new TestSuite;
foreach ($testSuiteNodes as $testSuiteNode) {
$suite->addTestSuite(
$this->getTestSuite($testSuiteNode, $testSuiteFilter)
);
}
return $suite;
}
/**
* Returns the test suite names from the configuration.
*/
public function getTestSuiteNames(): array
{
$names = [];
foreach ($this->xpath->query('*/testsuite') as $node) {
/* @var DOMElement $node */
$names[] = $node->getAttribute('name');
}
return $names;
}
private function validateConfigurationAgainstSchema(): void
{
$original = \libxml_use_internal_errors(true);
$xsdFilename = __DIR__ . '/../../phpunit.xsd';
if (\defined('__PHPUNIT_PHAR_ROOT__')) {
$xsdFilename = __PHPUNIT_PHAR_ROOT__ . '/phpunit.xsd';
}
$this->document->schemaValidate($xsdFilename);
$this->errors = \libxml_get_errors();
\libxml_clear_errors();
\libxml_use_internal_errors($original);
}
/**
* Collects and returns the configuration arguments from the PHPUnit
* XML configuration
*/
private function getConfigurationArguments(\DOMNodeList $nodes): array
{
$arguments = [];
if ($nodes->length === 0) {
return $arguments;
}
foreach ($nodes as $node) {
if (!$node instanceof DOMElement) {
continue;
}
if ($node->tagName !== 'arguments') {
continue;
}
foreach ($node->childNodes as $argument) {
if (!$argument instanceof DOMElement) {
continue;
}
if ($argument->tagName === 'file' || $argument->tagName === 'directory') {
$arguments[] = $this->toAbsolutePath((string) $argument->textContent);
} else {
$arguments[] = Xml::xmlToVariable($argument);
}
}
}
return $arguments;
}
/**
* @throws \PHPUnit\Framework\Exception
*/
private function getTestSuite(DOMElement $testSuiteNode, string $testSuiteFilter = ''): TestSuite
{
if ($testSuiteNode->hasAttribute('name')) {
$suite = new TestSuite(
(string) $testSuiteNode->getAttribute('name')
);
} else {
$suite = new TestSuite;
}
$exclude = [];
foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) {
$excludeFile = (string) $excludeNode->textContent;
if ($excludeFile) {
$exclude[] = $this->toAbsolutePath($excludeFile);
}
}
$fileIteratorFacade = new FileIteratorFacade;
$testSuiteFilter = $testSuiteFilter ? \explode(',', $testSuiteFilter) : [];
foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) {
\assert($directoryNode instanceof DOMElement);
if (!empty($testSuiteFilter) && !\in_array($directoryNode->parentNode->getAttribute('name'), $testSuiteFilter)) {
continue;
}
$directory = (string) $directoryNode->textContent;
if (empty($directory)) {
continue;
}
if (!$this->satisfiesPhpVersion($directoryNode)) {
continue;
}
$files = $fileIteratorFacade->getFilesAsArray(
$this->toAbsolutePath($directory),
$directoryNode->hasAttribute('suffix') ? (string) $directoryNode->getAttribute('suffix') : 'Test.php',
$directoryNode->hasAttribute('prefix') ? (string) $directoryNode->getAttribute('prefix') : '',
$exclude
);
$suite->addTestFiles($files);
}
foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) {
\assert($fileNode instanceof DOMElement);
if (!empty($testSuiteFilter) && !\in_array($fileNode->parentNode->getAttribute('name'), $testSuiteFilter)) {
continue;
}
$file = (string) $fileNode->textContent;
if (empty($file)) {
continue;
}
$file = $fileIteratorFacade->getFilesAsArray(
$this->toAbsolutePath($file)
);
if (!isset($file[0])) {
continue;
}
$file = $file[0];
if (!$this->satisfiesPhpVersion($fileNode)) {
continue;
}
$suite->addTestFile($file);
}
return $suite;
}
private function satisfiesPhpVersion(DOMElement $node): bool
{
$phpVersion = \PHP_VERSION;
$phpVersionOperator = '>=';
if ($node->hasAttribute('phpVersion')) {
$phpVersion = (string) $node->getAttribute('phpVersion');
}
if ($node->hasAttribute('phpVersionOperator')) {
$phpVersionOperator = (string) $node->getAttribute('phpVersionOperator');
}
return \version_compare(\PHP_VERSION, $phpVersion, $phpVersionOperator);
}
/**
* if $value is 'false' or 'true', this returns the value that $value represents.
* Otherwise, returns $default, which may be a string in rare cases.
* See PHPUnit\Util\ConfigurationTest::testPHPConfigurationIsReadCorrectly
*
* @param bool|string $default
*
* @return bool|string
*/
private function getBoolean(string $value, $default)
{
if (\strtolower($value) === 'false') {
return false;
}
if (\strtolower($value) === 'true') {
return true;
}
return $default;
}
private function getInteger(string $value, int $default): int
{
if (\is_numeric($value)) {
return (int) $value;
}
return $default;
}
private function readFilterDirectories(string $query): array
{
$directories = [];
foreach ($this->xpath->query($query) as $directoryNode) {
\assert($directoryNode instanceof DOMElement);
$directoryPath = (string) $directoryNode->textContent;
if (!$directoryPath) {
continue;
}
$directories[] = [
'path' => $this->toAbsolutePath($directoryPath),
'prefix' => $directoryNode->hasAttribute('prefix') ? (string) $directoryNode->getAttribute('prefix') : '',
'suffix' => $directoryNode->hasAttribute('suffix') ? (string) $directoryNode->getAttribute('suffix') : '.php',
'group' => $directoryNode->hasAttribute('group') ? (string) $directoryNode->getAttribute('group') : 'DEFAULT',
];
}
return $directories;
}
/**
* @return string[]
*/
private function readFilterFiles(string $query): array
{
$files = [];
foreach ($this->xpath->query($query) as $file) {
$filePath = (string) $file->textContent;
if ($filePath) {
$files[] = $this->toAbsolutePath($filePath);
}
}
return $files;
}
private function toAbsolutePath(string $path, bool $useIncludePath = false): string
{
$path = \trim($path);
if (\strpos($path, '/') === 0) {
return $path;
}
// Matches the following on Windows:
// - \\NetworkComputer\Path
// - \\.\D:
// - \\.\c:
// - C:\Windows
// - C:\windows
// - C:/windows
// - c:/windows
if (\defined('PHP_WINDOWS_VERSION_BUILD') &&
($path[0] === '\\' || (\strlen($path) >= 3 && \preg_match('#^[A-Z]\:[/\\\]#i', \substr($path, 0, 3))))) {
return $path;
}
if (\strpos($path, '://') !== false) {
return $path;
}
$file = \dirname($this->filename) . \DIRECTORY_SEPARATOR . $path;
if ($useIncludePath && !\file_exists($file)) {
$includePathFile = \stream_resolve_include_path($path);
if ($includePathFile) {
$file = $includePathFile;
}
}
return $file;
}
private function parseGroupConfiguration(string $root): array
{
$groups = [
'include' => [],
'exclude' => [],
];
foreach ($this->xpath->query($root . '/include/group') as $group) {
$groups['include'][] = (string) $group->textContent;
}
foreach ($this->xpath->query($root . '/exclude/group') as $group) {
$groups['exclude'][] = (string) $group->textContent;
}
return $groups;
}
private function getElementConfigurationParameters(DOMElement $element): array
{
$class = (string) $element->getAttribute('class');
$file = '';
$arguments = $this->getConfigurationArguments($element->childNodes);
if ($element->getAttribute('file')) {
$file = $this->toAbsolutePath(
(string) $element->getAttribute('file'),
true
);
}
return [
'class' => $class,
'file' => $file,
'arguments' => $arguments,
];
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ConfigurationGenerator
{
/**
* @var string
*/
private const TEMPLATE = <<<EOT
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/{phpunit_version}/phpunit.xsd"
bootstrap="{bootstrap_script}"
executionOrder="depends,defects"
forceCoversAnnotation="true"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
verbose="true">
<testsuites>
<testsuite name="default">
<directory suffix="Test.php">{tests_directory}</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">{src_directory}</directory>
</whitelist>
</filter>
</phpunit>
EOT;
public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory): string
{
return \str_replace(
[
'{phpunit_version}',
'{bootstrap_script}',
'{tests_directory}',
'{src_directory}',
],
[
$phpunitVersion,
$bootstrapScript,
$testsDirectory,
$srcDirectory,
],
self::TEMPLATE
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Type
{
public static function isType(string $type): bool
{
switch ($type) {
case 'numeric':
case 'integer':
case 'int':
case 'iterable':
case 'float':
case 'string':
case 'boolean':
case 'bool':
case 'null':
case 'array':
case 'object':
case 'resource':
case 'scalar':
return true;
default:
return false;
}
}
public static function isCloneable(object $object): bool
{
try {
$clone = clone $object;
} catch (\Throwable $t) {
return false;
}
return $clone instanceof $object;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Getopt
{
/**
* @throws Exception
*/
public static function getopt(array $args, string $short_options, array $long_options = null): array
{
if (empty($args)) {
return [[], []];
}
$opts = [];
$non_opts = [];
if ($long_options) {
\sort($long_options);
}
if (isset($args[0][0]) && $args[0][0] !== '-') {
\array_shift($args);
}
\reset($args);
$args = \array_map('trim', $args);
/* @noinspection ComparisonOperandsOrderInspection */
while (false !== $arg = \current($args)) {
$i = \key($args);
\next($args);
if ($arg === '') {
continue;
}
if ($arg === '--') {
$non_opts = \array_merge($non_opts, \array_slice($args, $i + 1));
break;
}
if ($arg[0] !== '-' || (\strlen($arg) > 1 && $arg[1] === '-' && !$long_options)) {
$non_opts[] = $args[$i];
continue;
}
if (\strlen($arg) > 1 && $arg[1] === '-') {
self::parseLongOption(
\substr($arg, 2),
$long_options,
$opts,
$args
);
} else {
self::parseShortOption(
\substr($arg, 1),
$short_options,
$opts,
$args
);
}
}
return [$opts, $non_opts];
}
/**
* @throws Exception
*/
private static function parseShortOption(string $arg, string $short_options, array &$opts, array &$args): void
{
$argLen = \strlen($arg);
for ($i = 0; $i < $argLen; $i++) {
$opt = $arg[$i];
$opt_arg = null;
if ($arg[$i] === ':' || ($spec = \strstr($short_options, $opt)) === false) {
throw new Exception(
"unrecognized option -- $opt"
);
}
if (\strlen($spec) > 1 && $spec[1] === ':') {
if ($i + 1 < $argLen) {
$opts[] = [$opt, \substr($arg, $i + 1)];
break;
}
if (!(\strlen($spec) > 2 && $spec[2] === ':')) {
/* @noinspection ComparisonOperandsOrderInspection */
if (false === $opt_arg = \current($args)) {
throw new Exception(
"option requires an argument -- $opt"
);
}
\next($args);
}
}
$opts[] = [$opt, $opt_arg];
}
}
/**
* @throws Exception
*/
private static function parseLongOption(string $arg, array $long_options, array &$opts, array &$args): void
{
$count = \count($long_options);
$list = \explode('=', $arg);
$opt = $list[0];
$opt_arg = null;
if (\count($list) > 1) {
$opt_arg = $list[1];
}
$opt_len = \strlen($opt);
foreach ($long_options as $i => $long_opt) {
$opt_start = \substr($long_opt, 0, $opt_len);
if ($opt_start !== $opt) {
continue;
}
$opt_rest = \substr($long_opt, $opt_len);
if ($opt_rest !== '' && $i + 1 < $count && $opt[0] !== '=' && \strpos($long_options[$i + 1], $opt) === 0) {
throw new Exception(
"option --$opt is ambiguous"
);
}
if (\substr($long_opt, -1) === '=') {
/* @noinspection StrlenInEmptyStringCheckContextInspection */
if (\substr($long_opt, -2) !== '==' && !\strlen((string) $opt_arg)) {
/* @noinspection ComparisonOperandsOrderInspection */
if (false === $opt_arg = \current($args)) {
throw new Exception(
"option --$opt requires an argument"
);
}
\next($args);
}
} elseif ($opt_arg) {
throw new Exception(
"option --$opt doesn't allow an argument"
);
}
$full_option = '--' . \preg_replace('/={1,2}$/', '', $long_opt);
$opts[] = [$full_option, $opt_arg];
return;
}
throw new Exception("unrecognized option --$opt");
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Error\Deprecated;
use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ErrorHandler
{
/**
* @var bool
*/
private $convertDeprecationsToExceptions;
/**
* @var bool
*/
private $convertErrorsToExceptions;
/**
* @var bool
*/
private $convertNoticesToExceptions;
/**
* @var bool
*/
private $convertWarningsToExceptions;
/**
* @var bool
*/
private $registered = false;
public static function invokeIgnoringWarnings(callable $callable)
{
\set_error_handler(
static function ($errorNumber, $errorString) {
if ($errorNumber === \E_WARNING) {
return;
}
return false;
}
);
$result = $callable();
\restore_error_handler();
return $result;
}
public function __construct(bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions)
{
$this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions;
$this->convertErrorsToExceptions = $convertErrorsToExceptions;
$this->convertNoticesToExceptions = $convertNoticesToExceptions;
$this->convertWarningsToExceptions = $convertWarningsToExceptions;
}
public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool
{
/*
* Do not raise an exception when the error suppression operator (@) was used.
*
* @see https://github.com/sebastianbergmann/phpunit/issues/3739
*/
if (!($errorNumber & \error_reporting())) {
return false;
}
switch ($errorNumber) {
case \E_NOTICE:
case \E_USER_NOTICE:
case \E_STRICT:
if (!$this->convertNoticesToExceptions) {
return false;
}
throw new Notice($errorString, $errorNumber, $errorFile, $errorLine);
case \E_WARNING:
case \E_USER_WARNING:
if (!$this->convertWarningsToExceptions) {
return false;
}
throw new Warning($errorString, $errorNumber, $errorFile, $errorLine);
case \E_DEPRECATED:
case \E_USER_DEPRECATED:
if (!$this->convertDeprecationsToExceptions) {
return false;
}
throw new Deprecated($errorString, $errorNumber, $errorFile, $errorLine);
default:
if (!$this->convertErrorsToExceptions) {
return false;
}
throw new Error($errorString, $errorNumber, $errorFile, $errorLine);
}
}
public function register(): void
{
if ($this->registered) {
return;
}
$oldErrorHandler = \set_error_handler($this);
if ($oldErrorHandler !== null) {
\restore_error_handler();
return;
}
$this->registered = true;
}
public function unregister(): void
{
if (!$this->registered) {
return;
}
\restore_error_handler();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\PhptTestCase;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class XmlTestListRenderer
{
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function render(TestSuite $suite): string
{
$writer = new \XMLWriter;
$writer->openMemory();
$writer->setIndent(true);
$writer->startDocument();
$writer->startElement('tests');
$currentTestCase = null;
foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) {
if ($test instanceof TestCase) {
if (\get_class($test) !== $currentTestCase) {
if ($currentTestCase !== null) {
$writer->endElement();
}
$writer->startElement('testCaseClass');
$writer->writeAttribute('name', \get_class($test));
$currentTestCase = \get_class($test);
}
$writer->startElement('testCaseMethod');
$writer->writeAttribute('name', $test->getName(false));
$writer->writeAttribute('groups', \implode(',', $test->getGroups()));
if (!empty($test->getDataSetAsString(false))) {
$writer->writeAttribute(
'dataSet',
\str_replace(
' with data set ',
'',
$test->getDataSetAsString(false)
)
);
}
$writer->endElement();
} elseif ($test instanceof PhptTestCase) {
if ($currentTestCase !== null) {
$writer->endElement();
$currentTestCase = null;
}
$writer->startElement('phptFile');
$writer->writeAttribute('path', $test->getName());
$writer->endElement();
}
}
if ($currentTestCase !== null) {
$writer->endElement();
}
$writer->endElement();
return $writer->outputMemory();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\PhptTestCase;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TextTestListRenderer
{
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function render(TestSuite $suite): string
{
$buffer = 'Available test(s):' . \PHP_EOL;
foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) {
if ($test instanceof TestCase) {
$name = \sprintf(
'%s::%s',
\get_class($test),
\str_replace(' with data set ', '', $test->getName())
);
} elseif ($test instanceof PhptTestCase) {
$name = $test->getName();
} else {
continue;
}
$buffer .= \sprintf(
' - %s' . \PHP_EOL,
$name
);
}
return $buffer;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use Composer\Autoload\ClassLoader;
use DeepCopy\DeepCopy;
use Doctrine\Instantiator\Instantiator;
use PharIo\Manifest\Manifest;
use PharIo\Version\Version as PharIoVersion;
use PHP_Token;
use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\Project;
use phpDocumentor\Reflection\Type;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophet;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeUnitReverseLookup\Wizard;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Diff\Diff;
use SebastianBergmann\Environment\Runtime;
use SebastianBergmann\Exporter\Exporter;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
use SebastianBergmann\GlobalState\Snapshot;
use SebastianBergmann\Invoker\Invoker;
use SebastianBergmann\ObjectEnumerator\Enumerator;
use SebastianBergmann\RecursionContext\Context;
use SebastianBergmann\ResourceOperations\ResourceOperations;
use SebastianBergmann\Timer\Timer;
use SebastianBergmann\Type\TypeName;
use SebastianBergmann\Version;
use Text_Template;
use TheSeer\Tokenizer\Tokenizer;
use Webmozart\Assert\Assert;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Blacklist
{
/**
* @var array<string,int>
*/
public static $blacklistedClassNames = [
// composer
ClassLoader::class => 1,
// doctrine/instantiator
Instantiator::class => 1,
// myclabs/deepcopy
DeepCopy::class => 1,
// phar-io/manifest
Manifest::class => 1,
// phar-io/version
PharIoVersion::class => 1,
// phpdocumentor/reflection-common
Project::class => 1,
// phpdocumentor/reflection-docblock
DocBlock::class => 1,
// phpdocumentor/type-resolver
Type::class => 1,
// phpspec/prophecy
Prophet::class => 1,
// phpunit/phpunit
TestCase::class => 2,
// phpunit/php-code-coverage
CodeCoverage::class => 1,
// phpunit/php-file-iterator
FileIteratorFacade::class => 1,
// phpunit/php-invoker
Invoker::class => 1,
// phpunit/php-text-template
Text_Template::class => 1,
// phpunit/php-timer
Timer::class => 1,
// phpunit/php-token-stream
PHP_Token::class => 1,
// sebastian/code-unit-reverse-lookup
Wizard::class => 1,
// sebastian/comparator
Comparator::class => 1,
// sebastian/diff
Diff::class => 1,
// sebastian/environment
Runtime::class => 1,
// sebastian/exporter
Exporter::class => 1,
// sebastian/global-state
Snapshot::class => 1,
// sebastian/object-enumerator
Enumerator::class => 1,
// sebastian/recursion-context
Context::class => 1,
// sebastian/resource-operations
ResourceOperations::class => 1,
// sebastian/type
TypeName::class => 1,
// sebastian/version
Version::class => 1,
// theseer/tokenizer
Tokenizer::class => 1,
// webmozart/assert
Assert::class => 1,
];
/**
* @var string[]
*/
private static $directories;
/**
* @throws Exception
*
* @return string[]
*/
public function getBlacklistedDirectories(): array
{
$this->initialize();
return self::$directories;
}
/**
* @throws Exception
*/
public function isBlacklisted(string $file): bool
{
if (\defined('PHPUNIT_TESTSUITE')) {
return false;
}
$this->initialize();
foreach (self::$directories as $directory) {
if (\strpos($file, $directory) === 0) {
return true;
}
}
return false;
}
/**
* @throws Exception
*/
private function initialize(): void
{
if (self::$directories === null) {
self::$directories = [];
foreach (self::$blacklistedClassNames as $className => $parent) {
if (!\class_exists($className)) {
continue;
}
try {
$directory = (new \ReflectionClass($className))->getFileName();
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
for ($i = 0; $i < $parent; $i++) {
$directory = \dirname($directory);
}
self::$directories[] = $directory;
}
// Hide process isolation workaround on Windows.
if (\DIRECTORY_SEPARATOR === '\\') {
// tempnam() prefix is limited to first 3 chars.
// @see https://php.net/manual/en/function.tempnam.php
self::$directories[] = \sys_get_temp_dir() . '\\PHP';
}
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Util;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\SyntheticError;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Filter
{
/**
* @throws Exception
*/
public static function getFilteredStacktrace(\Throwable $t): string
{
$prefix = false;
$script = \realpath($GLOBALS['_SERVER']['SCRIPT_NAME']);
if (\defined('__PHPUNIT_PHAR_ROOT__')) {
$prefix = __PHPUNIT_PHAR_ROOT__;
}
$filteredStacktrace = '';
if ($t instanceof SyntheticError) {
$eTrace = $t->getSyntheticTrace();
$eFile = $t->getSyntheticFile();
$eLine = $t->getSyntheticLine();
} elseif ($t instanceof Exception) {
$eTrace = $t->getSerializableTrace();
$eFile = $t->getFile();
$eLine = $t->getLine();
} else {
if ($t->getPrevious()) {
$t = $t->getPrevious();
}
$eTrace = $t->getTrace();
$eFile = $t->getFile();
$eLine = $t->getLine();
}
if (!self::frameExists($eTrace, $eFile, $eLine)) {
\array_unshift(
$eTrace,
['file' => $eFile, 'line' => $eLine]
);
}
$blacklist = new Blacklist;
foreach ($eTrace as $frame) {
if (isset($frame['file']) && \is_file($frame['file']) &&
(empty($GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST']) || !\in_array($frame['file'], $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'])) &&
!$blacklist->isBlacklisted($frame['file']) &&
($prefix === false || \strpos($frame['file'], $prefix) !== 0) &&
$frame['file'] !== $script) {
$filteredStacktrace .= \sprintf(
"%s:%s\n",
$frame['file'],
$frame['line'] ?? '?'
);
}
}
return $filteredStacktrace;
}
private static function frameExists(array $trace, string $file, int $line): bool
{
foreach ($trace as $frame) {
if (isset($frame['file']) && $frame['file'] === $file &&
isset($frame['line']) && $frame['line'] === $line) {
return true;
}
}
return false;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
use SebastianBergmann\Version as VersionId;
final class Version
{
/**
* @var string
*/
private static $pharVersion = '';
/**
* @var string
*/
private static $version = '';
/**
* Returns the current version of PHPUnit.
*/
public static function id(): string
{
if (self::$pharVersion !== '') {
return self::$pharVersion;
}
if (self::$version === '') {
self::$version = (new VersionId('8.4.3', \dirname(__DIR__, 2)))->getVersion();
}
return self::$version;
}
public static function series(): string
{
if (\strpos(self::id(), '-')) {
$version = \explode('-', self::id())[0];
} else {
$version = self::id();
}
return \implode('.', \array_slice(\explode('.', $version), 0, 2));
}
public static function getVersionString(): string
{
return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.';
}
public static function getReleaseChannel(): string
{
if (\strpos(self::$pharVersion, '-') !== false) {
return '-nightly';
}
return '';
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestSuite;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
abstract class BaseTestRunner
{
/**
* @var int
*/
public const STATUS_UNKNOWN = -1;
/**
* @var int
*/
public const STATUS_PASSED = 0;
/**
* @var int
*/
public const STATUS_SKIPPED = 1;
/**
* @var int
*/
public const STATUS_INCOMPLETE = 2;
/**
* @var int
*/
public const STATUS_FAILURE = 3;
/**
* @var int
*/
public const STATUS_ERROR = 4;
/**
* @var int
*/
public const STATUS_RISKY = 5;
/**
* @var int
*/
public const STATUS_WARNING = 6;
/**
* @var string
*/
public const SUITE_METHODNAME = 'suite';
/**
* Returns the loader to be used.
*/
public function getLoader(): TestSuiteLoader
{
return new StandardTestSuiteLoader;
}
/**
* Returns the Test corresponding to the given suite.
* This is a template method, subclasses override
* the runFailed() and clearStatus() methods.
*
* @param string|string[] $suffixes
*
* @throws Exception
*/
public function getTest(string $suiteClassName, string $suiteClassFile = '', $suffixes = ''): ?Test
{
if (empty($suiteClassFile) && \is_dir($suiteClassName) && !\is_file($suiteClassName . '.php')) {
/** @var string[] $files */
$files = (new FileIteratorFacade)->getFilesAsArray(
$suiteClassName,
$suffixes
);
$suite = new TestSuite($suiteClassName);
$suite->addTestFiles($files);
return $suite;
}
try {
$testClass = $this->loadSuiteClass(
$suiteClassName,
$suiteClassFile
);
} catch (Exception $e) {
$this->runFailed($e->getMessage());
return null;
}
try {
$suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME);
if (!$suiteMethod->isStatic()) {
$this->runFailed(
'suite() method must be static.'
);
return null;
}
$test = $suiteMethod->invoke(null, $testClass->getName());
} catch (\ReflectionException $e) {
try {
$test = new TestSuite($testClass);
} catch (Exception $e) {
$test = new TestSuite;
$test->setName($suiteClassName);
}
}
$this->clearStatus();
return $test;
}
/**
* Returns the loaded ReflectionClass for a suite name.
*/
protected function loadSuiteClass(string $suiteClassName, string $suiteClassFile = ''): \ReflectionClass
{
return $this->getLoader()->load($suiteClassName, $suiteClassFile);
}
/**
* Clears the status message.
*/
protected function clearStatus(): void
{
}
/**
* Override to define how to handle a failed loading of
* a test suite.
*/
abstract protected function runFailed(string $message): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
use PHPUnit\Util\ErrorHandler;
use PHPUnit\Util\Filesystem;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class DefaultTestResultCache implements \Serializable, TestResultCache
{
/**
* @var string
*/
public const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache';
/**
* Provide extra protection against incomplete or corrupt caches
*
* @var int[]
*/
private const ALLOWED_CACHE_TEST_STATUSES = [
BaseTestRunner::STATUS_SKIPPED,
BaseTestRunner::STATUS_INCOMPLETE,
BaseTestRunner::STATUS_FAILURE,
BaseTestRunner::STATUS_ERROR,
BaseTestRunner::STATUS_RISKY,
BaseTestRunner::STATUS_WARNING,
];
/**
* Path and filename for result cache file
*
* @var string
*/
private $cacheFilename;
/**
* The list of defective tests
*
* <code>
* // Mark a test skipped
* $this->defects[$testName] = BaseTestRunner::TEST_SKIPPED;
* </code>
*
* @var array<string, int>
*/
private $defects = [];
/**
* The list of execution duration of suites and tests (in seconds)
*
* <code>
* // Record running time for test
* $this->times[$testName] = 1.234;
* </code>
*
* @var array<string, float>
*/
private $times = [];
public function __construct(?string $filepath = null)
{
if ($filepath !== null && \is_dir($filepath)) {
// cache path provided, use default cache filename in that location
$filepath .= \DIRECTORY_SEPARATOR . self::DEFAULT_RESULT_CACHE_FILENAME;
}
$this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME;
}
/**
* @throws Exception
*/
public function persist(): void
{
$this->saveToFile();
}
/**
* @throws Exception
*/
public function saveToFile(): void
{
if (\defined('PHPUNIT_TESTSUITE_RESULTCACHE')) {
return;
}
if (!Filesystem::createDirectory(\dirname($this->cacheFilename))) {
throw new Exception(
\sprintf(
'Cannot create directory "%s" for result cache file',
$this->cacheFilename
)
);
}
\file_put_contents(
$this->cacheFilename,
\serialize($this)
);
}
public function setState(string $testName, int $state): void
{
if ($state !== BaseTestRunner::STATUS_PASSED) {
$this->defects[$testName] = $state;
}
}
public function getState(string $testName): int
{
return $this->defects[$testName] ?? BaseTestRunner::STATUS_UNKNOWN;
}
public function setTime(string $testName, float $time): void
{
$this->times[$testName] = $time;
}
public function getTime(string $testName): float
{
return $this->times[$testName] ?? 0.0;
}
public function load(): void
{
$this->clear();
if (!\is_file($this->cacheFilename)) {
return;
}
$cacheData = @\file_get_contents($this->cacheFilename);
// @codeCoverageIgnoreStart
if ($cacheData === false) {
return;
}
// @codeCoverageIgnoreEnd
$cache = ErrorHandler::invokeIgnoringWarnings(
static function () use ($cacheData) {
return @\unserialize($cacheData, ['allowed_classes' => [self::class]]);
}
);
if ($cache === false) {
return;
}
if ($cache instanceof self) {
/* @var DefaultTestResultCache $cache */
$cache->copyStateToCache($this);
}
}
public function copyStateToCache(self $targetCache): void
{
foreach ($this->defects as $name => $state) {
$targetCache->setState($name, $state);
}
foreach ($this->times as $name => $time) {
$targetCache->setTime($name, $time);
}
}
public function clear(): void
{
$this->defects = [];
$this->times = [];
}
public function serialize(): string
{
return \serialize([
'defects' => $this->defects,
'times' => $this->times,
]);
}
/**
* @param string $serialized
*/
public function unserialize($serialized): void
{
$data = \unserialize($serialized);
if (isset($data['times'])) {
foreach ($data['times'] as $testName => $testTime) {
\assert(\is_string($testName));
\assert(\is_float($testTime));
$this->times[$testName] = $testTime;
}
}
if (isset($data['defects'])) {
foreach ($data['defects'] as $testName => $testResult) {
\assert(\is_string($testName));
\assert(\is_int($testResult));
if (\in_array($testResult, self::ALLOWED_CACHE_TEST_STATUSES, true)) {
$this->defects[$testName] = $testResult;
}
}
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner\Filter;
use FilterIterator;
use InvalidArgumentException;
use Iterator;
use PHPUnit\Framework\TestSuite;
use ReflectionClass;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Factory
{
/**
* @var array
*/
private $filters = [];
/**
* @throws InvalidArgumentException
*/
public function addFilter(ReflectionClass $filter, $args): void
{
if (!$filter->isSubclassOf(\RecursiveFilterIterator::class)) {
throw new InvalidArgumentException(
\sprintf(
'Class "%s" does not extend RecursiveFilterIterator',
$filter->name
)
);
}
$this->filters[] = [$filter, $args];
}
public function factory(Iterator $iterator, TestSuite $suite): FilterIterator
{
foreach ($this->filters as $filter) {
[$class, $args] = $filter;
$iterator = $class->newInstance($iterator, $args, $suite);
}
return $iterator;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner\Filter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ExcludeGroupFilterIterator extends GroupFilterIterator
{
protected function doAccept(string $hash): bool
{
return !\in_array($hash, $this->groupTests, true);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner\Filter;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Util\RegularExpression;
use RecursiveFilterIterator;
use RecursiveIterator;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class NameFilterIterator extends RecursiveFilterIterator
{
/**
* @var string
*/
private $filter;
/**
* @var int
*/
private $filterMin;
/**
* @var int
*/
private $filterMax;
/**
* @throws \Exception
*/
public function __construct(RecursiveIterator $iterator, string $filter)
{
parent::__construct($iterator);
$this->setFilter($filter);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function accept(): bool
{
$test = $this->getInnerIterator()->current();
if ($test instanceof TestSuite) {
return true;
}
$tmp = \PHPUnit\Util\Test::describe($test);
if ($test instanceof WarningTestCase) {
$name = $test->getMessage();
} elseif ($tmp[0] !== '') {
$name = \implode('::', $tmp);
} else {
$name = $tmp[1];
}
$accepted = @\preg_match($this->filter, $name, $matches);
if ($accepted && isset($this->filterMax)) {
$set = \end($matches);
$accepted = $set >= $this->filterMin && $set <= $this->filterMax;
}
return (bool) $accepted;
}
/**
* @throws \Exception
*/
private function setFilter(string $filter): void
{
if (RegularExpression::safeMatch($filter, '') === false) {
// Handles:
// * testAssertEqualsSucceeds#4
// * testAssertEqualsSucceeds#4-8
if (\preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) {
if (isset($matches[3]) && $matches[2] < $matches[3]) {
$filter = \sprintf(
'%s.*with data set #(\d+)$',
$matches[1]
);
$this->filterMin = $matches[2];
$this->filterMax = $matches[3];
} else {
$filter = \sprintf(
'%s.*with data set #%s$',
$matches[1],
$matches[2]
);
}
} // Handles:
// * testDetermineJsonError@JSON_ERROR_NONE
// * testDetermineJsonError@JSON.*
elseif (\preg_match('/^(.*?)@(.+)$/', $filter, $matches)) {
$filter = \sprintf(
'%s.*with data set "%s"$',
$matches[1],
$matches[2]
);
}
// Escape delimiters in regular expression. Do NOT use preg_quote,
// to keep magic characters.
$filter = \sprintf('/%s/i', \str_replace(
'/',
'\\/',
$filter
));
}
$this->filter = $filter;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner\Filter;
use PHPUnit\Framework\TestSuite;
use RecursiveFilterIterator;
use RecursiveIterator;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
abstract class GroupFilterIterator extends RecursiveFilterIterator
{
/**
* @var string[]
*/
protected $groupTests = [];
public function __construct(RecursiveIterator $iterator, array $groups, TestSuite $suite)
{
parent::__construct($iterator);
foreach ($suite->getGroupDetails() as $group => $tests) {
if (\in_array((string) $group, $groups, true)) {
$testHashes = \array_map(
'spl_object_hash',
$tests
);
$this->groupTests = \array_merge($this->groupTests, $testHashes);
}
}
}
public function accept(): bool
{
$test = $this->getInnerIterator()->current();
if ($test instanceof TestSuite) {
return true;
}
return $this->doAccept(\spl_object_hash($test));
}
abstract protected function doAccept(string $hash);
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner\Filter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class IncludeGroupFilterIterator extends GroupFilterIterator
{
protected function doAccept(string $hash): bool
{
return \in_array($hash, $this->groupTests, true);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Exception extends \RuntimeException implements \PHPUnit\Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ResultCacheExtension implements AfterIncompleteTestHook, AfterLastTestHook, AfterRiskyTestHook, AfterSkippedTestHook, AfterSuccessfulTestHook, AfterTestErrorHook, AfterTestFailureHook, AfterTestWarningHook
{
/**
* @var TestResultCache
*/
private $cache;
public function __construct(TestResultCache $cache)
{
$this->cache = $cache;
}
public function flush(): void
{
$this->cache->persist();
}
public function executeAfterSuccessfulTest(string $test, float $time): void
{
$testName = $this->getTestName($test);
$this->cache->setTime($testName, \round($time, 3));
}
public function executeAfterIncompleteTest(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);
$this->cache->setTime($testName, \round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_INCOMPLETE);
}
public function executeAfterRiskyTest(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);
$this->cache->setTime($testName, \round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_RISKY);
}
public function executeAfterSkippedTest(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);
$this->cache->setTime($testName, \round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_SKIPPED);
}
public function executeAfterTestError(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);
$this->cache->setTime($testName, \round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_ERROR);
}
public function executeAfterTestFailure(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);
$this->cache->setTime($testName, \round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_FAILURE);
}
public function executeAfterTestWarning(string $test, string $message, float $time): void
{
$testName = $this->getTestName($test);
$this->cache->setTime($testName, \round($time, 3));
$this->cache->setState($testName, BaseTestRunner::STATUS_WARNING);
}
public function executeAfterLastTest(): void
{
$this->flush();
}
/**
* @param string $test A long description format of the current test
*
* @return string The test name without TestSuiteClassName:: and @dataprovider details
*/
private function getTestName(string $test): string
{
$matches = [];
if (\preg_match('/^(?<name>\S+::\S+)(?:(?<dataname> with data set (?:#\d+|"[^"]+"))\s\()?/', $test, $matches)) {
$test = $matches['name'] . ($matches['dataname'] ?? '');
}
return $test;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface TestResultCache
{
public function setState(string $testName, int $state): void;
public function getState(string $testName): int;
public function setTime(string $testName, float $time): void;
public function getTime(string $testName): float;
public function load(): void;
public function persist(): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
use PHPUnit\Framework\DataProviderTestSuite;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Util\Test as TestUtil;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TestSuiteSorter
{
/**
* @var int
*/
public const ORDER_DEFAULT = 0;
/**
* @var int
*/
public const ORDER_RANDOMIZED = 1;
/**
* @var int
*/
public const ORDER_REVERSED = 2;
/**
* @var int
*/
public const ORDER_DEFECTS_FIRST = 3;
/**
* @var int
*/
public const ORDER_DURATION = 4;
/**
* Order tests by @size annotation 'small', 'medium', 'large'
*
* @var int
*/
public const ORDER_SIZE = 5;
/**
* List of sorting weights for all test result codes. A higher number gives higher priority.
*/
private const DEFECT_SORT_WEIGHT = [
BaseTestRunner::STATUS_ERROR => 6,
BaseTestRunner::STATUS_FAILURE => 5,
BaseTestRunner::STATUS_WARNING => 4,
BaseTestRunner::STATUS_INCOMPLETE => 3,
BaseTestRunner::STATUS_RISKY => 2,
BaseTestRunner::STATUS_SKIPPED => 1,
BaseTestRunner::STATUS_UNKNOWN => 0,
];
private const SIZE_SORT_WEIGHT = [
TestUtil::SMALL => 1,
TestUtil::MEDIUM => 2,
TestUtil::LARGE => 3,
TestUtil::UNKNOWN => 4,
];
/**
* @var array<string, int> Associative array of (string => DEFECT_SORT_WEIGHT) elements
*/
private $defectSortOrder = [];
/**
* @var TestResultCache
*/
private $cache;
/**
* @var string[] A list of normalized names of tests before reordering
*/
private $originalExecutionOrder = [];
/**
* @var string[] A list of normalized names of tests affected by reordering
*/
private $executionOrder = [];
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function getTestSorterUID(Test $test): string
{
if ($test instanceof PhptTestCase) {
return $test->getName();
}
if ($test instanceof TestCase) {
$testName = $test->getName(true);
if (\strpos($testName, '::') === false) {
$testName = \get_class($test) . '::' . $testName;
}
return $testName;
}
return $test->getName();
}
public function __construct(?TestResultCache $cache = null)
{
$this->cache = $cache ?? new NullTestResultCache;
}
/**
* @throws Exception
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = true): void
{
$allowedOrders = [
self::ORDER_DEFAULT,
self::ORDER_REVERSED,
self::ORDER_RANDOMIZED,
self::ORDER_DURATION,
self::ORDER_SIZE,
];
if (!\in_array($order, $allowedOrders, true)) {
throw new Exception(
'$order must be one of TestSuiteSorter::ORDER_[DEFAULT|REVERSED|RANDOMIZED|DURATION|SIZE]'
);
}
$allowedOrderDefects = [
self::ORDER_DEFAULT,
self::ORDER_DEFECTS_FIRST,
];
if (!\in_array($orderDefects, $allowedOrderDefects, true)) {
throw new Exception(
'$orderDefects must be one of TestSuiteSorter::ORDER_DEFAULT, TestSuiteSorter::ORDER_DEFECTS_FIRST'
);
}
if ($isRootTestSuite) {
$this->originalExecutionOrder = $this->calculateTestExecutionOrder($suite);
}
if ($suite instanceof TestSuite) {
foreach ($suite as $_suite) {
$this->reorderTestsInSuite($_suite, $order, $resolveDependencies, $orderDefects, false);
}
if ($orderDefects === self::ORDER_DEFECTS_FIRST) {
$this->addSuiteToDefectSortOrder($suite);
}
$this->sort($suite, $order, $resolveDependencies, $orderDefects);
}
if ($isRootTestSuite) {
$this->executionOrder = $this->calculateTestExecutionOrder($suite);
}
}
public function getOriginalExecutionOrder(): array
{
return $this->originalExecutionOrder;
}
public function getExecutionOrder(): array
{
return $this->executionOrder;
}
private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects): void
{
if (empty($suite->tests())) {
return;
}
if ($order === self::ORDER_REVERSED) {
$suite->setTests($this->reverse($suite->tests()));
} elseif ($order === self::ORDER_RANDOMIZED) {
$suite->setTests($this->randomize($suite->tests()));
} elseif ($order === self::ORDER_DURATION && $this->cache !== null) {
$suite->setTests($this->sortByDuration($suite->tests()));
} elseif ($order === self::ORDER_SIZE) {
$suite->setTests($this->sortBySize($suite->tests()));
}
if ($orderDefects === self::ORDER_DEFECTS_FIRST && $this->cache !== null) {
$suite->setTests($this->sortDefectsFirst($suite->tests()));
}
if ($resolveDependencies && !($suite instanceof DataProviderTestSuite) && $this->suiteOnlyContainsTests($suite)) {
/** @var TestCase[] $tests */
$tests = $suite->tests();
$suite->setTests($this->resolveDependencies($tests));
}
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function addSuiteToDefectSortOrder(TestSuite $suite): void
{
$max = 0;
foreach ($suite->tests() as $test) {
$testname = self::getTestSorterUID($test);
if (!isset($this->defectSortOrder[$testname])) {
$this->defectSortOrder[$testname] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($testname)];
$max = \max($max, $this->defectSortOrder[$testname]);
}
}
$this->defectSortOrder[$suite->getName()] = $max;
}
private function suiteOnlyContainsTests(TestSuite $suite): bool
{
return \array_reduce(
$suite->tests(),
static function ($carry, $test) {
return $carry && ($test instanceof TestCase || $test instanceof DataProviderTestSuite);
},
true
);
}
private function reverse(array $tests): array
{
return \array_reverse($tests);
}
private function randomize(array $tests): array
{
\shuffle($tests);
return $tests;
}
private function sortDefectsFirst(array $tests): array
{
\usort(
$tests,
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
function ($left, $right) {
return $this->cmpDefectPriorityAndTime($left, $right);
}
);
return $tests;
}
private function sortByDuration(array $tests): array
{
\usort(
$tests,
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
function ($left, $right) {
return $this->cmpDuration($left, $right);
}
);
return $tests;
}
private function sortBySize(array $tests): array
{
\usort(
$tests,
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
function ($left, $right) {
return $this->cmpSize($left, $right);
}
);
return $tests;
}
/**
* Comparator callback function to sort tests for "reach failure as fast as possible":
* 1. sort tests by defect weight defined in self::DEFECT_SORT_WEIGHT
* 2. when tests are equally defective, sort the fastest to the front
* 3. do not reorder successful tests
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function cmpDefectPriorityAndTime(Test $a, Test $b): int
{
$priorityA = $this->defectSortOrder[self::getTestSorterUID($a)] ?? 0;
$priorityB = $this->defectSortOrder[self::getTestSorterUID($b)] ?? 0;
if ($priorityB <=> $priorityA) {
// Sort defect weight descending
return $priorityB <=> $priorityA;
}
if ($priorityA || $priorityB) {
return $this->cmpDuration($a, $b);
}
// do not change execution order
return 0;
}
/**
* Compares test duration for sorting tests by duration ascending.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function cmpDuration(Test $a, Test $b): int
{
return $this->cache->getTime(self::getTestSorterUID($a)) <=> $this->cache->getTime(self::getTestSorterUID($b));
}
/**
* Compares test size for sorting tests small->medium->large->unknown
*/
private function cmpSize(Test $a, Test $b): int
{
$sizeA = ($a instanceof TestCase || $a instanceof DataProviderTestSuite)
? $a->getSize()
: TestUtil::UNKNOWN;
$sizeB = ($b instanceof TestCase || $b instanceof DataProviderTestSuite)
? $b->getSize()
: TestUtil::UNKNOWN;
return self::SIZE_SORT_WEIGHT[$sizeA] <=> self::SIZE_SORT_WEIGHT[$sizeB];
}
/**
* Reorder Tests within a TestCase in such a way as to resolve as many dependencies as possible.
* The algorithm will leave the tests in original running order when it can.
* For more details see the documentation for test dependencies.
*
* Short description of algorithm:
* 1. Pick the next Test from remaining tests to be checked for dependencies.
* 2. If the test has no dependencies: mark done, start again from the top
* 3. If the test has dependencies but none left to do: mark done, start again from the top
* 4. When we reach the end add any leftover tests to the end. These will be marked 'skipped' during execution.
*
* @param array<DataProviderTestSuite|TestCase> $tests
*
* @return array<DataProviderTestSuite|TestCase>
*/
private function resolveDependencies(array $tests): array
{
$newTestOrder = [];
$i = 0;
do {
$todoNames = \array_map(
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
static function ($test) {
return self::getTestSorterUID($test);
},
$tests
);
if (!$tests[$i]->hasDependencies() || empty(\array_intersect($this->getNormalizedDependencyNames($tests[$i]), $todoNames))) {
$newTestOrder = \array_merge($newTestOrder, \array_splice($tests, $i, 1));
$i = 0;
} else {
$i++;
}
} while (!empty($tests) && ($i < \count($tests)));
return \array_merge($newTestOrder, $tests);
}
/**
* @param DataProviderTestSuite|TestCase $test
*
* @return array<string> A list of full test names as "TestSuiteClassName::testMethodName"
*/
private function getNormalizedDependencyNames($test): array
{
if ($test instanceof DataProviderTestSuite) {
$testClass = \substr($test->getName(), 0, \strpos($test->getName(), '::'));
} else {
$testClass = \get_class($test);
}
$names = \array_map(
static function ($name) use ($testClass) {
return \strpos($name, '::') === false ? $testClass . '::' . $name : $name;
},
$test->getDependencies()
);
return $names;
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function calculateTestExecutionOrder(Test $suite): array
{
$tests = [];
if ($suite instanceof TestSuite) {
foreach ($suite->tests() as $test) {
if (!($test instanceof TestSuite)) {
$tests[] = self::getTestSorterUID($test);
} else {
$tests = \array_merge($tests, $this->calculateTestExecutionOrder($test));
}
}
}
return $tests;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterTestWarningHook extends TestHook
{
public function executeAfterTestWarning(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterSkippedTestHook extends TestHook
{
public function executeAfterSkippedTest(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface Hook
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface TestHook extends Hook
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface BeforeTestHook extends TestHook
{
public function executeBeforeTest(string $test): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterTestErrorHook extends TestHook
{
public function executeAfterTestError(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Test as TestUtil;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TestListenerAdapter implements TestListener
{
/**
* @var TestHook[]
*/
private $hooks = [];
/**
* @var bool
*/
private $lastTestWasNotSuccessful;
public function add(TestHook $hook): void
{
$this->hooks[] = $hook;
}
public function startTest(Test $test): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof BeforeTestHook) {
$hook->executeBeforeTest(TestUtil::describeAsString($test));
}
}
$this->lastTestWasNotSuccessful = false;
}
public function addError(Test $test, \Throwable $t, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterTestErrorHook) {
$hook->executeAfterTestError(TestUtil::describeAsString($test), $t->getMessage(), $time);
}
}
$this->lastTestWasNotSuccessful = true;
}
public function addWarning(Test $test, Warning $e, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterTestWarningHook) {
$hook->executeAfterTestWarning(TestUtil::describeAsString($test), $e->getMessage(), $time);
}
}
$this->lastTestWasNotSuccessful = true;
}
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterTestFailureHook) {
$hook->executeAfterTestFailure(TestUtil::describeAsString($test), $e->getMessage(), $time);
}
}
$this->lastTestWasNotSuccessful = true;
}
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterIncompleteTestHook) {
$hook->executeAfterIncompleteTest(TestUtil::describeAsString($test), $t->getMessage(), $time);
}
}
$this->lastTestWasNotSuccessful = true;
}
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterRiskyTestHook) {
$hook->executeAfterRiskyTest(TestUtil::describeAsString($test), $t->getMessage(), $time);
}
}
$this->lastTestWasNotSuccessful = true;
}
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
{
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterSkippedTestHook) {
$hook->executeAfterSkippedTest(TestUtil::describeAsString($test), $t->getMessage(), $time);
}
}
$this->lastTestWasNotSuccessful = true;
}
public function endTest(Test $test, float $time): void
{
if (!$this->lastTestWasNotSuccessful) {
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterSuccessfulTestHook) {
$hook->executeAfterSuccessfulTest(TestUtil::describeAsString($test), $time);
}
}
}
foreach ($this->hooks as $hook) {
if ($hook instanceof AfterTestHook) {
$hook->executeAfterTest(TestUtil::describeAsString($test), $time);
}
}
}
public function startTestSuite(TestSuite $suite): void
{
}
public function endTestSuite(TestSuite $suite): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface BeforeFirstTestHook extends Hook
{
public function executeBeforeFirstTest(): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterRiskyTestHook extends TestHook
{
public function executeAfterRiskyTest(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterLastTestHook extends Hook
{
public function executeAfterLastTest(): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterTestHook extends TestHook
{
/**
* This hook will fire after any test, regardless of the result.
*
* For more fine grained control, have a look at the other hooks
* that extend PHPUnit\Runner\Hook.
*/
public function executeAfterTest(string $test, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterTestFailureHook extends TestHook
{
public function executeAfterTestFailure(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterSuccessfulTestHook extends TestHook
{
public function executeAfterSuccessfulTest(string $test, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
interface AfterIncompleteTestHook extends TestHook
{
public function executeAfterIncompleteTest(string $test, string $message, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class NullTestResultCache implements TestResultCache
{
public function setState(string $testName, int $state): void
{
}
public function getState(string $testName): int
{
return BaseTestRunner::STATUS_UNKNOWN;
}
public function setTime(string $testName, float $time): void
{
}
public function getTime(string $testName): float
{
return 0;
}
public function load(): void
{
}
public function persist(): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
use ReflectionClass;
/**
* An interface to define how a test suite should be loaded.
*/
interface TestSuiteLoader
{
public function load(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass;
public function reload(ReflectionClass $aClass): ReflectionClass;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
use PHPUnit\Framework\TestCase;
use PHPUnit\Util\FileLoader;
use PHPUnit\Util\Filesystem;
use ReflectionClass;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class StandardTestSuiteLoader implements TestSuiteLoader
{
/**
* @throws Exception
* @throws \PHPUnit\Framework\Exception
*/
public function load(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass
{
$suiteClassName = \str_replace('.php', '', $suiteClassName);
$filename = null;
if (empty($suiteClassFile)) {
$suiteClassFile = Filesystem::classNameToFilename(
$suiteClassName
);
}
if (!\class_exists($suiteClassName, false)) {
$loadedClasses = \get_declared_classes();
$filename = FileLoader::checkAndLoad($suiteClassFile);
$loadedClasses = \array_values(
\array_diff(\get_declared_classes(), $loadedClasses)
);
}
if (!empty($loadedClasses) && !\class_exists($suiteClassName, false)) {
$offset = 0 - \strlen($suiteClassName);
foreach ($loadedClasses as $loadedClass) {
try {
$class = new ReflectionClass($loadedClass);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (\substr($loadedClass, $offset) === $suiteClassName &&
$class->getFileName() == $filename) {
$suiteClassName = $loadedClass;
break;
}
}
}
if (!empty($loadedClasses) && !\class_exists($suiteClassName, false)) {
$testCaseClass = TestCase::class;
foreach ($loadedClasses as $loadedClass) {
try {
$class = new ReflectionClass($loadedClass);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$classFile = $class->getFileName();
if ($class->isSubclassOf($testCaseClass) && !$class->isAbstract()) {
$suiteClassName = $loadedClass;
$testCaseClass = $loadedClass;
if ($classFile == \realpath($suiteClassFile)) {
break;
}
}
if ($class->hasMethod('suite')) {
try {
$method = $class->getMethod('suite');
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$method->isAbstract() && $method->isPublic() && $method->isStatic()) {
$suiteClassName = $loadedClass;
if ($classFile == \realpath($suiteClassFile)) {
break;
}
}
}
}
}
if (\class_exists($suiteClassName, false)) {
try {
$class = new ReflectionClass($suiteClassName);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($class->getFileName() == \realpath($suiteClassFile)) {
return $class;
}
}
throw new Exception(
\sprintf(
"Class '%s' could not be found in '%s'.",
$suiteClassName,
$suiteClassFile
)
);
}
public function reload(ReflectionClass $aClass): ReflectionClass
{
return $aClass;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\IncompleteTestError;
use PHPUnit\Framework\PHPTAssertionFailedError;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\SkippedTestError;
use PHPUnit\Framework\SyntheticSkippedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestResult;
use PHPUnit\Util\PHP\AbstractPhpProcess;
use SebastianBergmann\Timer\Timer;
use Text_Template;
use Throwable;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class PhptTestCase implements SelfDescribing, Test
{
/**
* @var string[]
*/
private const SETTINGS = [
'allow_url_fopen=1',
'auto_append_file=',
'auto_prepend_file=',
'disable_functions=',
'display_errors=1',
'docref_ext=.html',
'docref_root=',
'error_append_string=',
'error_prepend_string=',
'error_reporting=-1',
'html_errors=0',
'log_errors=0',
'magic_quotes_runtime=0',
'open_basedir=',
'output_buffering=Off',
'output_handler=',
'report_memleaks=0',
'report_zend_debug=0',
'safe_mode=0',
'xdebug.default_enable=0',
];
/**
* @var string
*/
private $filename;
/**
* @var AbstractPhpProcess
*/
private $phpUtil;
/**
* @var string
*/
private $output = '';
/**
* Constructs a test case with the given filename.
*
* @throws Exception
*/
public function __construct(string $filename, AbstractPhpProcess $phpUtil = null)
{
if (!\is_file($filename)) {
throw new Exception(
\sprintf(
'File "%s" does not exist.',
$filename
)
);
}
$this->filename = $filename;
$this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory();
}
/**
* Counts the number of test cases executed by run(TestResult result).
*/
public function count(): int
{
return 1;
}
/**
* Runs a test and collects its result in a TestResult instance.
*
* @throws Exception
* @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException
* @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException
* @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException
* @throws \SebastianBergmann\CodeCoverage\RuntimeException
* @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function run(TestResult $result = null): TestResult
{
if ($result === null) {
$result = new TestResult;
}
try {
$sections = $this->parse();
} catch (Exception $e) {
$result->startTest($this);
$result->addFailure($this, new SkippedTestError($e->getMessage()), 0);
$result->endTest($this, 0);
return $result;
}
$code = $this->render($sections['FILE']);
$xfail = false;
$settings = $this->parseIniSection(self::SETTINGS);
$result->startTest($this);
if (isset($sections['INI'])) {
$settings = $this->parseIniSection($sections['INI'], $settings);
}
if (isset($sections['ENV'])) {
$env = $this->parseEnvSection($sections['ENV']);
$this->phpUtil->setEnv($env);
}
$this->phpUtil->setUseStderrRedirection(true);
if ($result->enforcesTimeLimit()) {
$this->phpUtil->setTimeout($result->getTimeoutForLargeTests());
}
$skip = $this->runSkip($sections, $result, $settings);
if ($skip) {
return $result;
}
if (isset($sections['XFAIL'])) {
$xfail = \trim($sections['XFAIL']);
}
if (isset($sections['STDIN'])) {
$this->phpUtil->setStdin($sections['STDIN']);
}
if (isset($sections['ARGS'])) {
$this->phpUtil->setArgs($sections['ARGS']);
}
if ($result->getCollectCodeCoverageInformation()) {
$this->renderForCoverage($code);
}
Timer::start();
$jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings));
$time = Timer::stop();
$this->output = $jobResult['stdout'] ?? '';
if ($result->getCollectCodeCoverageInformation() && ($coverage = $this->cleanupForCoverage())) {
$result->getCodeCoverage()->append($coverage, $this, true, [], [], true);
}
try {
$this->assertPhptExpectation($sections, $this->output);
} catch (AssertionFailedError $e) {
$failure = $e;
if ($xfail !== false) {
$failure = new IncompleteTestError($xfail, 0, $e);
} elseif ($e instanceof ExpectationFailedException) {
$comparisonFailure = $e->getComparisonFailure();
if ($comparisonFailure) {
$diff = $comparisonFailure->getDiff();
} else {
$diff = $e->getMessage();
}
$hint = $this->getLocationHintFromDiff($diff, $sections);
$trace = \array_merge($hint, \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
$failure = new PHPTAssertionFailedError(
$e->getMessage(),
0,
$trace[0]['file'],
$trace[0]['line'],
$trace,
$comparisonFailure ? $diff : ''
);
}
$result->addFailure($this, $failure, $time);
} catch (Throwable $t) {
$result->addError($this, $t, $time);
}
if ($xfail !== false && $result->allCompletelyImplemented()) {
$result->addFailure($this, new IncompleteTestError('XFAIL section but test passes'), $time);
}
$this->runClean($sections);
$result->endTest($this, $time);
return $result;
}
/**
* Returns the name of the test case.
*/
public function getName(): string
{
return $this->toString();
}
/**
* Returns a string representation of the test case.
*/
public function toString(): string
{
return $this->filename;
}
public function usesDataProvider(): bool
{
return false;
}
public function getNumAssertions(): int
{
return 1;
}
public function getActualOutput(): string
{
return $this->output;
}
public function hasOutput(): bool
{
return !empty($this->output);
}
/**
* Parse --INI-- section key value pairs and return as array.
*
* @param array|string
*/
private function parseIniSection($content, $ini = []): array
{
if (\is_string($content)) {
$content = \explode("\n", \trim($content));
}
foreach ($content as $setting) {
if (\strpos($setting, '=') === false) {
continue;
}
$setting = \explode('=', $setting, 2);
$name = \trim($setting[0]);
$value = \trim($setting[1]);
if ($name === 'extension' || $name === 'zend_extension') {
if (!isset($ini[$name])) {
$ini[$name] = [];
}
$ini[$name][] = $value;
continue;
}
$ini[$name] = $value;
}
return $ini;
}
private function parseEnvSection(string $content): array
{
$env = [];
foreach (\explode("\n", \trim($content)) as $e) {
$e = \explode('=', \trim($e), 2);
if (!empty($e[0]) && isset($e[1])) {
$env[$e[0]] = $e[1];
}
}
return $env;
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
private function assertPhptExpectation(array $sections, string $output): void
{
$assertions = [
'EXPECT' => 'assertEquals',
'EXPECTF' => 'assertStringMatchesFormat',
'EXPECTREGEX' => 'assertRegExp',
];
$actual = \preg_replace('/\r\n/', "\n", \trim($output));
foreach ($assertions as $sectionName => $sectionAssertion) {
if (isset($sections[$sectionName])) {
$sectionContent = \preg_replace('/\r\n/', "\n", \trim($sections[$sectionName]));
$expected = $sectionName === 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent;
if ($expected === null) {
throw new Exception('No PHPT expectation found');
}
Assert::$sectionAssertion($expected, $actual);
return;
}
}
throw new Exception('No PHPT assertion found');
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function runSkip(array &$sections, TestResult $result, array $settings): bool
{
if (!isset($sections['SKIPIF'])) {
return false;
}
$skipif = $this->render($sections['SKIPIF']);
$jobResult = $this->phpUtil->runJob($skipif, $this->stringifyIni($settings));
if (!\strncasecmp('skip', \ltrim($jobResult['stdout']), 4)) {
$message = '';
if (\preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $skipMatch)) {
$message = \substr($skipMatch[1], 2);
}
$hint = $this->getLocationHint($message, $sections, 'SKIPIF');
$trace = \array_merge($hint, \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
$result->addFailure(
$this,
new SyntheticSkippedError($message, 0, $trace[0]['file'], $trace[0]['line'], $trace),
0
);
$result->endTest($this, 0);
return true;
}
return false;
}
private function runClean(array &$sections): void
{
$this->phpUtil->setStdin('');
$this->phpUtil->setArgs('');
if (isset($sections['CLEAN'])) {
$cleanCode = $this->render($sections['CLEAN']);
$this->phpUtil->runJob($cleanCode, self::SETTINGS);
}
}
/**
* @throws Exception
*/
private function parse(): array
{
$sections = [];
$section = '';
$unsupportedSections = [
'CGI',
'COOKIE',
'DEFLATE_POST',
'EXPECTHEADERS',
'EXTENSIONS',
'GET',
'GZIP_POST',
'HEADERS',
'PHPDBG',
'POST',
'POST_RAW',
'PUT',
'REDIRECTTEST',
'REQUEST',
];
$lineNr = 0;
foreach (\file($this->filename) as $line) {
$lineNr++;
if (\preg_match('/^--([_A-Z]+)--/', $line, $result)) {
$section = $result[1];
$sections[$section] = '';
$sections[$section . '_offset'] = $lineNr;
continue;
}
if (empty($section)) {
throw new Exception('Invalid PHPT file: empty section header');
}
$sections[$section] .= $line;
}
if (isset($sections['FILEEOF'])) {
$sections['FILE'] = \rtrim($sections['FILEEOF'], "\r\n");
unset($sections['FILEEOF']);
}
$this->parseExternal($sections);
if (!$this->validate($sections)) {
throw new Exception('Invalid PHPT file');
}
foreach ($unsupportedSections as $section) {
if (isset($sections[$section])) {
throw new Exception(
"PHPUnit does not support PHPT $section sections"
);
}
}
return $sections;
}
/**
* @throws Exception
*/
private function parseExternal(array &$sections): void
{
$allowSections = [
'FILE',
'EXPECT',
'EXPECTF',
'EXPECTREGEX',
];
$testDirectory = \dirname($this->filename) . \DIRECTORY_SEPARATOR;
foreach ($allowSections as $section) {
if (isset($sections[$section . '_EXTERNAL'])) {
$externalFilename = \trim($sections[$section . '_EXTERNAL']);
if (!\is_file($testDirectory . $externalFilename) ||
!\is_readable($testDirectory . $externalFilename)) {
throw new Exception(
\sprintf(
'Could not load --%s-- %s for PHPT file',
$section . '_EXTERNAL',
$testDirectory . $externalFilename
)
);
}
$sections[$section] = \file_get_contents($testDirectory . $externalFilename);
}
}
}
private function validate(array &$sections): bool
{
$requiredSections = [
'FILE',
[
'EXPECT',
'EXPECTF',
'EXPECTREGEX',
],
];
foreach ($requiredSections as $section) {
if (\is_array($section)) {
$foundSection = false;
foreach ($section as $anySection) {
if (isset($sections[$anySection])) {
$foundSection = true;
break;
}
}
if (!$foundSection) {
return false;
}
continue;
}
if (!isset($sections[$section])) {
return false;
}
}
return true;
}
private function render(string $code): string
{
return \str_replace(
[
'__DIR__',
'__FILE__',
],
[
"'" . \dirname($this->filename) . "'",
"'" . $this->filename . "'",
],
$code
);
}
private function getCoverageFiles(): array
{
$baseDir = \dirname(\realpath($this->filename)) . \DIRECTORY_SEPARATOR;
$basename = \basename($this->filename, 'phpt');
return [
'coverage' => $baseDir . $basename . 'coverage',
'job' => $baseDir . $basename . 'php',
];
}
private function renderForCoverage(string &$job): void
{
$files = $this->getCoverageFiles();
$template = new Text_Template(
__DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl'
);
$composerAutoload = '\'\'';
if (\defined('PHPUNIT_COMPOSER_INSTALL') && !\defined('PHPUNIT_TESTSUITE')) {
$composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, true);
}
$phar = '\'\'';
if (\defined('__PHPUNIT_PHAR__')) {
$phar = \var_export(__PHPUNIT_PHAR__, true);
}
$globals = '';
if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
$globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export(
$GLOBALS['__PHPUNIT_BOOTSTRAP'],
true
) . ";\n";
}
$template->setVar(
[
'composerAutoload' => $composerAutoload,
'phar' => $phar,
'globals' => $globals,
'job' => $files['job'],
'coverageFile' => $files['coverage'],
]
);
\file_put_contents($files['job'], $job);
$job = $template->render();
}
private function cleanupForCoverage(): array
{
$files = $this->getCoverageFiles();
$coverage = @\unserialize(\file_get_contents($files['coverage']));
if ($coverage === false) {
$coverage = [];
}
foreach ($files as $file) {
@\unlink($file);
}
return $coverage;
}
private function stringifyIni(array $ini): array
{
$settings = [];
foreach ($ini as $key => $value) {
if (\is_array($value)) {
foreach ($value as $val) {
$settings[] = $key . '=' . $val;
}
continue;
}
$settings[] = $key . '=' . $value;
}
return $settings;
}
private function getLocationHintFromDiff(string $message, array $sections): array
{
$needle = '';
$previousLine = '';
$block = 'message';
foreach (\preg_split('/\r\n|\r|\n/', $message) as $line) {
$line = \trim($line);
if ($block === 'message' && $line === '--- Expected') {
$block = 'expected';
}
if ($block === 'expected' && $line === '@@ @@') {
$block = 'diff';
}
if ($block === 'diff') {
if (\strpos($line, '+') === 0) {
$needle = $this->getCleanDiffLine($previousLine);
break;
}
if (\strpos($line, '-') === 0) {
$needle = $this->getCleanDiffLine($line);
break;
}
}
if (!empty($line)) {
$previousLine = $line;
}
}
return $this->getLocationHint($needle, $sections);
}
private function getCleanDiffLine(string $line): string
{
if (\preg_match('/^[\-+]([\'\"]?)(.*)\1$/', $line, $matches)) {
$line = $matches[2];
}
return $line;
}
private function getLocationHint(string $needle, array $sections, ?string $sectionName = null): array
{
$needle = \trim($needle);
if (empty($needle)) {
return [[
'file' => \realpath($this->filename),
'line' => 1,
]];
}
if ($sectionName) {
$search = [$sectionName];
} else {
$search = [
// 'FILE',
'EXPECT',
'EXPECTF',
'EXPECTREGEX',
];
}
foreach ($search as $section) {
if (!isset($sections[$section])) {
continue;
}
if (isset($sections[$section . '_EXTERNAL'])) {
$externalFile = \trim($sections[$section . '_EXTERNAL']);
return [
[
'file' => \realpath(\dirname($this->filename) . \DIRECTORY_SEPARATOR . $externalFile),
'line' => 1,
],
[
'file' => \realpath($this->filename),
'line' => ($sections[$section . '_EXTERNAL_offset'] ?? 0) + 1,
],
];
}
$sectionOffset = $sections[$section . '_offset'] ?? 0;
$offset = $sectionOffset + 1;
foreach (\preg_split('/\r\n|\r|\n/', $sections[$section]) as $line) {
if (\strpos($line, $needle) !== false) {
return [[
'file' => \realpath($this->filename),
'line' => $offset,
]];
}
$offset++;
}
}
if ($sectionName) {
// String not found in specified section, show user the start of the named section
return [[
'file' => \realpath($this->filename),
'line' => $sectionOffset,
]];
}
// No section specified, show user start of code
return [[
'file' => \realpath($this->filename),
'line' => 1,
]];
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\Filter\Factory;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\FileLoader;
use PHPUnit\Util\Test as TestUtil;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class TestSuite implements \IteratorAggregate, SelfDescribing, Test
{
/**
* Enable or disable the backup and restoration of the $GLOBALS array.
*
* @var bool
*/
protected $backupGlobals;
/**
* Enable or disable the backup and restoration of static attributes.
*
* @var bool
*/
protected $backupStaticAttributes;
/**
* @var bool
*/
protected $runTestInSeparateProcess = false;
/**
* The name of the test suite.
*
* @var string
*/
protected $name = '';
/**
* The test groups of the test suite.
*
* @var array
*/
protected $groups = [];
/**
* The tests in the test suite.
*
* @var Test[]
*/
protected $tests = [];
/**
* The number of tests in the test suite.
*
* @var int
*/
protected $numTests = -1;
/**
* @var bool
*/
protected $testCase = false;
/**
* @var string[]
*/
protected $foundClasses = [];
/**
* Last count of tests in this suite.
*
* @var null|int
*/
private $cachedNumTests;
/**
* @var bool
*/
private $beStrictAboutChangesToGlobalState;
/**
* @var Factory
*/
private $iteratorFilter;
/**
* @var string[]
*/
private $declaredClasses;
/**
* Constructs a new TestSuite:
*
* - PHPUnit\Framework\TestSuite() constructs an empty TestSuite.
*
* - PHPUnit\Framework\TestSuite(ReflectionClass) constructs a
* TestSuite from the given class.
*
* - PHPUnit\Framework\TestSuite(ReflectionClass, String)
* constructs a TestSuite from the given class with the given
* name.
*
* - PHPUnit\Framework\TestSuite(String) either constructs a
* TestSuite from the given class (if the passed string is the
* name of an existing class) or constructs an empty TestSuite
* with the given name.
*
* @param \ReflectionClass|string $theClass
*
* @throws Exception
*/
public function __construct($theClass = '', string $name = '')
{
if (!\is_string($theClass) && !$theClass instanceof \ReflectionClass) {
throw InvalidArgumentException::create(
1,
'ReflectionClass object or string'
);
}
$this->declaredClasses = \get_declared_classes();
if (!$theClass instanceof \ReflectionClass) {
if (\class_exists($theClass, true)) {
if ($name === '') {
$name = $theClass;
}
try {
$theClass = new \ReflectionClass($theClass);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
} else {
$this->setName($theClass);
return;
}
}
if (!$theClass->isSubclassOf(TestCase::class)) {
$this->setName((string) $theClass);
return;
}
if ($name !== '') {
$this->setName($name);
} else {
$this->setName($theClass->getName());
}
$constructor = $theClass->getConstructor();
if ($constructor !== null &&
!$constructor->isPublic()) {
$this->addTest(
new WarningTestCase(
\sprintf(
'Class "%s" has no public constructor.',
$theClass->getName()
)
)
);
return;
}
foreach ($theClass->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() === Assert::class) {
continue;
}
if ($method->getDeclaringClass()->getName() === TestCase::class) {
continue;
}
$this->addTestMethod($theClass, $method);
}
if (empty($this->tests)) {
$this->addTest(
new WarningTestCase(
\sprintf(
'No tests found in class "%s".',
$theClass->getName()
)
)
);
}
$this->testCase = true;
}
/**
* Returns a string representation of the test suite.
*/
public function toString(): string
{
return $this->getName();
}
/**
* Adds a test to the suite.
*
* @param array $groups
*/
public function addTest(Test $test, $groups = []): void
{
try {
$class = new \ReflectionClass($test);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$class->isAbstract()) {
$this->tests[] = $test;
$this->numTests = -1;
if ($test instanceof self && empty($groups)) {
$groups = $test->getGroups();
}
if (empty($groups)) {
$groups = ['default'];
}
foreach ($groups as $group) {
if (!isset($this->groups[$group])) {
$this->groups[$group] = [$test];
} else {
$this->groups[$group][] = $test;
}
}
if ($test instanceof TestCase) {
$test->setGroups($groups);
}
}
}
/**
* Adds the tests from the given class to the suite.
*
* @param object|string $testClass
*
* @throws Exception
*/
public function addTestSuite($testClass): void
{
if (!(\is_object($testClass) || (\is_string($testClass) && \class_exists($testClass)))) {
throw InvalidArgumentException::create(
1,
'class name or object'
);
}
if (!\is_object($testClass)) {
try {
$testClass = new \ReflectionClass($testClass);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
}
if ($testClass instanceof self) {
$this->addTest($testClass);
} elseif ($testClass instanceof \ReflectionClass) {
$suiteMethod = false;
if (!$testClass->isAbstract() && $testClass->hasMethod(BaseTestRunner::SUITE_METHODNAME)) {
try {
$method = $testClass->getMethod(
BaseTestRunner::SUITE_METHODNAME
);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($method->isStatic()) {
$this->addTest(
$method->invoke(null, $testClass->getName())
);
$suiteMethod = true;
}
}
if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(TestCase::class)) {
$this->addTest(new self($testClass));
}
} else {
throw new Exception;
}
}
/**
* Wraps both <code>addTest()</code> and <code>addTestSuite</code>
* as well as the separate import statements for the user's convenience.
*
* If the named file cannot be read or there are no new tests that can be
* added, a <code>PHPUnit\Framework\WarningTestCase</code> will be created instead,
* leaving the current test run untouched.
*
* @throws Exception
*/
public function addTestFile(string $filename): void
{
if (\file_exists($filename) && \substr($filename, -5) === '.phpt') {
$this->addTest(
new PhptTestCase($filename)
);
return;
}
// The given file may contain further stub classes in addition to the
// test class itself. Figure out the actual test class.
$filename = FileLoader::checkAndLoad($filename);
$newClasses = \array_diff(\get_declared_classes(), $this->declaredClasses);
// The diff is empty in case a parent class (with test methods) is added
// AFTER a child class that inherited from it. To account for that case,
// accumulate all discovered classes, so the parent class may be found in
// a later invocation.
if (!empty($newClasses)) {
// On the assumption that test classes are defined first in files,
// process discovered classes in approximate LIFO order, so as to
// avoid unnecessary reflection.
$this->foundClasses = \array_merge($newClasses, $this->foundClasses);
$this->declaredClasses = \get_declared_classes();
}
// The test class's name must match the filename, either in full, or as
// a PEAR/PSR-0 prefixed short name ('NameSpace_ShortName'), or as a
// PSR-1 local short name ('NameSpace\ShortName'). The comparison must be
// anchored to prevent false-positive matches (e.g., 'OtherShortName').
$shortName = \basename($filename, '.php');
$shortNameRegEx = '/(?:^|_|\\\\)' . \preg_quote($shortName, '/') . '$/';
foreach ($this->foundClasses as $i => $className) {
if (\preg_match($shortNameRegEx, $className)) {
try {
$class = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($class->getFileName() == $filename) {
$newClasses = [$className];
unset($this->foundClasses[$i]);
break;
}
}
}
foreach ($newClasses as $className) {
try {
$class = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (\dirname($class->getFileName()) === __DIR__) {
continue;
}
if (!$class->isAbstract()) {
if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) {
try {
$method = $class->getMethod(
BaseTestRunner::SUITE_METHODNAME
);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($method->isStatic()) {
$this->addTest($method->invoke(null, $className));
}
} elseif ($class->implementsInterface(Test::class)) {
$this->addTestSuite($class);
}
}
}
$this->numTests = -1;
}
/**
* Wrapper for addTestFile() that adds multiple test files.
*
* @throws Exception
*/
public function addTestFiles(iterable $fileNames): void
{
foreach ($fileNames as $filename) {
$this->addTestFile((string) $filename);
}
}
/**
* Counts the number of test cases that will be run by this test.
*/
public function count(bool $preferCache = false): int
{
if ($preferCache && $this->cachedNumTests !== null) {
return $this->cachedNumTests;
}
$numTests = 0;
foreach ($this as $test) {
$numTests += \count($test);
}
$this->cachedNumTests = $numTests;
return $numTests;
}
/**
* Returns the name of the suite.
*/
public function getName(): string
{
return $this->name;
}
/**
* Returns the test groups of the suite.
*/
public function getGroups(): array
{
return \array_keys($this->groups);
}
public function getGroupDetails(): array
{
return $this->groups;
}
/**
* Set tests groups of the test case
*/
public function setGroupDetails(array $groups): void
{
$this->groups = $groups;
}
/**
* Runs the tests and collects their result in a TestResult.
*
* @throws \PHPUnit\Framework\CodeCoverageException
* @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException
* @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException
* @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException
* @throws \SebastianBergmann\CodeCoverage\RuntimeException
* @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Warning
*/
public function run(TestResult $result = null): TestResult
{
if ($result === null) {
$result = $this->createResult();
}
if (\count($this) === 0) {
return $result;
}
/** @psalm-var class-string $className */
$className = $this->name;
$hookMethods = TestUtil::getHookMethods($className);
$result->startTestSuite($this);
try {
foreach ($hookMethods['beforeClass'] as $beforeClassMethod) {
if ($this->testCase &&
\class_exists($this->name, false) &&
\method_exists($this->name, $beforeClassMethod)) {
if ($missingRequirements = TestUtil::getMissingRequirements($this->name, $beforeClassMethod)) {
$this->markTestSuiteSkipped(\implode(\PHP_EOL, $missingRequirements));
}
\call_user_func([$this->name, $beforeClassMethod]);
}
}
} catch (SkippedTestSuiteError $error) {
foreach ($this->tests() as $test) {
$result->startTest($test);
$result->addFailure($test, $error, 0);
$result->endTest($test, 0);
}
$result->endTestSuite($this);
return $result;
} catch (\Throwable $t) {
foreach ($this->tests() as $test) {
if ($result->shouldStop()) {
break;
}
$result->startTest($test);
$result->addError($test, $t, 0);
$result->endTest($test, 0);
}
$result->endTestSuite($this);
return $result;
}
foreach ($this as $test) {
if ($result->shouldStop()) {
break;
}
if ($test instanceof TestCase || $test instanceof self) {
$test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState);
$test->setBackupGlobals($this->backupGlobals);
$test->setBackupStaticAttributes($this->backupStaticAttributes);
$test->setRunTestInSeparateProcess($this->runTestInSeparateProcess);
}
$test->run($result);
}
try {
foreach ($hookMethods['afterClass'] as $afterClassMethod) {
if ($this->testCase &&
\class_exists($this->name, false) &&
\method_exists($this->name, $afterClassMethod)) {
\call_user_func([$this->name, $afterClassMethod]);
}
}
} catch (\Throwable $t) {
$message = "Exception in {$this->name}::$afterClassMethod" . \PHP_EOL . $t->getMessage();
$error = new SyntheticError($message, 0, $t->getFile(), $t->getLine(), $t->getTrace());
$placeholderTest = clone $test;
$placeholderTest->setName($afterClassMethod);
$result->startTest($placeholderTest);
$result->addFailure($placeholderTest, $error, 0);
$result->endTest($placeholderTest, 0);
}
$result->endTestSuite($this);
return $result;
}
public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void
{
$this->runTestInSeparateProcess = $runTestInSeparateProcess;
}
public function setName(string $name): void
{
$this->name = $name;
}
/**
* Returns the test at the given index.
*
* @return false|Test
*/
public function testAt(int $index)
{
return $this->tests[$index] ?? false;
}
/**
* Returns the tests as an enumeration.
*
* @return Test[]
*/
public function tests(): array
{
return $this->tests;
}
/**
* Set tests of the test suite
*
* @param Test[] $tests
*/
public function setTests(array $tests): void
{
$this->tests = $tests;
}
/**
* Mark the test suite as skipped.
*
* @param string $message
*
* @throws SkippedTestSuiteError
*/
public function markTestSuiteSkipped($message = ''): void
{
throw new SkippedTestSuiteError($message);
}
/**
* @param bool $beStrictAboutChangesToGlobalState
*/
public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState): void
{
if (null === $this->beStrictAboutChangesToGlobalState && \is_bool($beStrictAboutChangesToGlobalState)) {
$this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
}
}
/**
* @param bool $backupGlobals
*/
public function setBackupGlobals($backupGlobals): void
{
if (null === $this->backupGlobals && \is_bool($backupGlobals)) {
$this->backupGlobals = $backupGlobals;
}
}
/**
* @param bool $backupStaticAttributes
*/
public function setBackupStaticAttributes($backupStaticAttributes): void
{
if (null === $this->backupStaticAttributes && \is_bool($backupStaticAttributes)) {
$this->backupStaticAttributes = $backupStaticAttributes;
}
}
/**
* Returns an iterator for this test suite.
*/
public function getIterator(): \Iterator
{
$iterator = new TestSuiteIterator($this);
if ($this->iteratorFilter !== null) {
$iterator = $this->iteratorFilter->factory($iterator, $this);
}
return $iterator;
}
public function injectFilter(Factory $filter): void
{
$this->iteratorFilter = $filter;
foreach ($this as $test) {
if ($test instanceof self) {
$test->injectFilter($filter);
}
}
}
/**
* Creates a default TestResult object.
*/
protected function createResult(): TestResult
{
return new TestResult;
}
/**
* @throws Exception
*/
protected function addTestMethod(\ReflectionClass $class, \ReflectionMethod $method): void
{
if (!TestUtil::isTestMethod($method)) {
return;
}
$methodName = $method->getName();
if (!$method->isPublic()) {
$this->addTest(
new WarningTestCase(
\sprintf(
'Test method "%s" in test class "%s" is not public.',
$methodName,
$class->getName()
)
)
);
return;
}
$test = (new TestBuilder)->build($class, $methodName);
if ($test instanceof TestCase || $test instanceof DataProviderTestSuite) {
$test->setDependencies(
TestUtil::getDependencies($class->getName(), $methodName)
);
}
$this->addTest(
$test,
TestUtil::getGroups($class->getName(), $methodName)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use Countable;
/**
* A Test can be run and collect its results.
*/
interface Test extends Countable
{
/**
* Runs a test and collects its result in a TestResult instance.
*/
public function run(TestResult $result = null): TestResult;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use AssertionError;
use Countable;
use Error;
use PHPUnit\Framework\MockObject\Exception as MockObjectException;
use PHPUnit\Util\Blacklist;
use PHPUnit\Util\ErrorHandler;
use PHPUnit\Util\Printer;
use PHPUnit\Util\Test as TestUtil;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException as OriginalCoveredCodeNotExecutedException;
use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException;
use SebastianBergmann\CodeCoverage\MissingCoversAnnotationException as OriginalMissingCoversAnnotationException;
use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException;
use SebastianBergmann\Invoker\Invoker;
use SebastianBergmann\Invoker\TimeoutException;
use SebastianBergmann\ResourceOperations\ResourceOperations;
use SebastianBergmann\Timer\Timer;
use Throwable;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TestResult implements Countable
{
/**
* @var array
*/
private $passed = [];
/**
* @var TestFailure[]
*/
private $errors = [];
/**
* @var TestFailure[]
*/
private $failures = [];
/**
* @var TestFailure[]
*/
private $warnings = [];
/**
* @var TestFailure[]
*/
private $notImplemented = [];
/**
* @var TestFailure[]
*/
private $risky = [];
/**
* @var TestFailure[]
*/
private $skipped = [];
/**
* @deprecated Use the `TestHook` interfaces instead
*
* @var TestListener[]
*/
private $listeners = [];
/**
* @var int
*/
private $runTests = 0;
/**
* @var float
*/
private $time = 0;
/**
* @var TestSuite
*/
private $topTestSuite;
/**
* Code Coverage information.
*
* @var CodeCoverage
*/
private $codeCoverage;
/**
* @var bool
*/
private $convertDeprecationsToExceptions = true;
/**
* @var bool
*/
private $convertErrorsToExceptions = true;
/**
* @var bool
*/
private $convertNoticesToExceptions = true;
/**
* @var bool
*/
private $convertWarningsToExceptions = true;
/**
* @var bool
*/
private $stop = false;
/**
* @var bool
*/
private $stopOnError = false;
/**
* @var bool
*/
private $stopOnFailure = false;
/**
* @var bool
*/
private $stopOnWarning = false;
/**
* @var bool
*/
private $beStrictAboutTestsThatDoNotTestAnything = true;
/**
* @var bool
*/
private $beStrictAboutOutputDuringTests = false;
/**
* @var bool
*/
private $beStrictAboutTodoAnnotatedTests = false;
/**
* @var bool
*/
private $beStrictAboutResourceUsageDuringSmallTests = false;
/**
* @var bool
*/
private $enforceTimeLimit = false;
/**
* @var int
*/
private $timeoutForSmallTests = 1;
/**
* @var int
*/
private $timeoutForMediumTests = 10;
/**
* @var int
*/
private $timeoutForLargeTests = 60;
/**
* @var bool
*/
private $stopOnRisky = false;
/**
* @var bool
*/
private $stopOnIncomplete = false;
/**
* @var bool
*/
private $stopOnSkipped = false;
/**
* @var bool
*/
private $lastTestFailed = false;
/**
* @var int
*/
private $defaultTimeLimit = 0;
/**
* @var bool
*/
private $stopOnDefect = false;
/**
* @var bool
*/
private $registerMockObjectsFromTestArgumentsRecursively = false;
/**
* @deprecated Use the `TestHook` interfaces instead
*
* @codeCoverageIgnore
*
* Registers a TestListener.
*/
public function addListener(TestListener $listener): void
{
$this->listeners[] = $listener;
}
/**
* @deprecated Use the `TestHook` interfaces instead
*
* @codeCoverageIgnore
*
* Unregisters a TestListener.
*/
public function removeListener(TestListener $listener): void
{
foreach ($this->listeners as $key => $_listener) {
if ($listener === $_listener) {
unset($this->listeners[$key]);
}
}
}
/**
* @deprecated Use the `TestHook` interfaces instead
*
* @codeCoverageIgnore
*
* Flushes all flushable TestListeners.
*/
public function flushListeners(): void
{
foreach ($this->listeners as $listener) {
if ($listener instanceof Printer) {
$listener->flush();
}
}
}
/**
* Adds an error to the list of errors.
*/
public function addError(Test $test, Throwable $t, float $time): void
{
if ($t instanceof RiskyTestError) {
$this->risky[] = new TestFailure($test, $t);
$notifyMethod = 'addRiskyTest';
if ($test instanceof TestCase) {
$test->markAsRisky();
}
if ($this->stopOnRisky || $this->stopOnDefect) {
$this->stop();
}
} elseif ($t instanceof IncompleteTest) {
$this->notImplemented[] = new TestFailure($test, $t);
$notifyMethod = 'addIncompleteTest';
if ($this->stopOnIncomplete) {
$this->stop();
}
} elseif ($t instanceof SkippedTest) {
$this->skipped[] = new TestFailure($test, $t);
$notifyMethod = 'addSkippedTest';
if ($this->stopOnSkipped) {
$this->stop();
}
} else {
$this->errors[] = new TestFailure($test, $t);
$notifyMethod = 'addError';
if ($this->stopOnError || $this->stopOnFailure) {
$this->stop();
}
}
// @see https://github.com/sebastianbergmann/phpunit/issues/1953
if ($t instanceof Error) {
$t = new ExceptionWrapper($t);
}
foreach ($this->listeners as $listener) {
$listener->$notifyMethod($test, $t, $time);
}
$this->lastTestFailed = true;
$this->time += $time;
}
/**
* Adds a warning to the list of warnings.
* The passed in exception caused the warning.
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
if ($this->stopOnWarning || $this->stopOnDefect) {
$this->stop();
}
$this->warnings[] = new TestFailure($test, $e);
foreach ($this->listeners as $listener) {
$listener->addWarning($test, $e, $time);
}
$this->time += $time;
}
/**
* Adds a failure to the list of failures.
* The passed in exception caused the failure.
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
if ($e instanceof RiskyTestError || $e instanceof OutputError) {
$this->risky[] = new TestFailure($test, $e);
$notifyMethod = 'addRiskyTest';
if ($test instanceof TestCase) {
$test->markAsRisky();
}
if ($this->stopOnRisky || $this->stopOnDefect) {
$this->stop();
}
} elseif ($e instanceof IncompleteTest) {
$this->notImplemented[] = new TestFailure($test, $e);
$notifyMethod = 'addIncompleteTest';
if ($this->stopOnIncomplete) {
$this->stop();
}
} elseif ($e instanceof SkippedTest) {
$this->skipped[] = new TestFailure($test, $e);
$notifyMethod = 'addSkippedTest';
if ($this->stopOnSkipped) {
$this->stop();
}
} else {
$this->failures[] = new TestFailure($test, $e);
$notifyMethod = 'addFailure';
if ($this->stopOnFailure || $this->stopOnDefect) {
$this->stop();
}
}
foreach ($this->listeners as $listener) {
$listener->$notifyMethod($test, $e, $time);
}
$this->lastTestFailed = true;
$this->time += $time;
}
/**
* Informs the result that a test suite will be started.
*/
public function startTestSuite(TestSuite $suite): void
{
if ($this->topTestSuite === null) {
$this->topTestSuite = $suite;
}
foreach ($this->listeners as $listener) {
$listener->startTestSuite($suite);
}
}
/**
* Informs the result that a test suite was completed.
*/
public function endTestSuite(TestSuite $suite): void
{
foreach ($this->listeners as $listener) {
$listener->endTestSuite($suite);
}
}
/**
* Informs the result that a test will be started.
*/
public function startTest(Test $test): void
{
$this->lastTestFailed = false;
$this->runTests += \count($test);
foreach ($this->listeners as $listener) {
$listener->startTest($test);
}
}
/**
* Informs the result that a test was completed.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function endTest(Test $test, float $time): void
{
foreach ($this->listeners as $listener) {
$listener->endTest($test, $time);
}
if (!$this->lastTestFailed && $test instanceof TestCase) {
$class = \get_class($test);
$key = $class . '::' . $test->getName();
$this->passed[$key] = [
'result' => $test->getResult(),
'size' => \PHPUnit\Util\Test::getSize(
$class,
$test->getName(false)
),
];
$this->time += $time;
}
}
/**
* Returns true if no risky test occurred.
*/
public function allHarmless(): bool
{
return $this->riskyCount() == 0;
}
/**
* Gets the number of risky tests.
*/
public function riskyCount(): int
{
return \count($this->risky);
}
/**
* Returns true if no incomplete test occurred.
*/
public function allCompletelyImplemented(): bool
{
return $this->notImplementedCount() == 0;
}
/**
* Gets the number of incomplete tests.
*/
public function notImplementedCount(): int
{
return \count($this->notImplemented);
}
/**
* Returns an array of TestFailure objects for the risky tests
*
* @return TestFailure[]
*/
public function risky(): array
{
return $this->risky;
}
/**
* Returns an array of TestFailure objects for the incomplete tests
*
* @return TestFailure[]
*/
public function notImplemented(): array
{
return $this->notImplemented;
}
/**
* Returns true if no test has been skipped.
*/
public function noneSkipped(): bool
{
return $this->skippedCount() == 0;
}
/**
* Gets the number of skipped tests.
*/
public function skippedCount(): int
{
return \count($this->skipped);
}
/**
* Returns an array of TestFailure objects for the skipped tests
*
* @return TestFailure[]
*/
public function skipped(): array
{
return $this->skipped;
}
/**
* Gets the number of detected errors.
*/
public function errorCount(): int
{
return \count($this->errors);
}
/**
* Returns an array of TestFailure objects for the errors
*
* @return TestFailure[]
*/
public function errors(): array
{
return $this->errors;
}
/**
* Gets the number of detected failures.
*/
public function failureCount(): int
{
return \count($this->failures);
}
/**
* Returns an array of TestFailure objects for the failures
*
* @return TestFailure[]
*/
public function failures(): array
{
return $this->failures;
}
/**
* Gets the number of detected warnings.
*/
public function warningCount(): int
{
return \count($this->warnings);
}
/**
* Returns an array of TestFailure objects for the warnings
*
* @return TestFailure[]
*/
public function warnings(): array
{
return $this->warnings;
}
/**
* Returns the names of the tests that have passed.
*/
public function passed(): array
{
return $this->passed;
}
/**
* Returns the (top) test suite.
*/
public function topTestSuite(): TestSuite
{
return $this->topTestSuite;
}
/**
* Returns whether code coverage information should be collected.
*/
public function getCollectCodeCoverageInformation(): bool
{
return $this->codeCoverage !== null;
}
/**
* Runs a TestCase.
*
* @throws CodeCoverageException
* @throws OriginalCoveredCodeNotExecutedException
* @throws OriginalMissingCoversAnnotationException
* @throws UnintentionallyCoveredCodeException
* @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException
* @throws \SebastianBergmann\CodeCoverage\RuntimeException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function run(Test $test): void
{
Assert::resetCount();
if ($test instanceof TestCase) {
$test->setRegisterMockObjectsFromTestArgumentsRecursively(
$this->registerMockObjectsFromTestArgumentsRecursively
);
$isAnyCoverageRequired = TestUtil::requiresCodeCoverageDataCollection($test);
}
$error = false;
$failure = false;
$warning = false;
$incomplete = false;
$risky = false;
$skipped = false;
$this->startTest($test);
if ($this->convertDeprecationsToExceptions || $this->convertErrorsToExceptions || $this->convertNoticesToExceptions || $this->convertWarningsToExceptions) {
$errorHandler = new ErrorHandler(
$this->convertDeprecationsToExceptions,
$this->convertErrorsToExceptions,
$this->convertNoticesToExceptions,
$this->convertWarningsToExceptions
);
$errorHandler->register();
}
$collectCodeCoverage = $this->codeCoverage !== null &&
!$test instanceof WarningTestCase &&
$isAnyCoverageRequired;
if ($collectCodeCoverage) {
$this->codeCoverage->start($test);
}
$monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests &&
!$test instanceof WarningTestCase &&
$test->getSize() == \PHPUnit\Util\Test::SMALL &&
\function_exists('xdebug_start_function_monitor');
if ($monitorFunctions) {
/* @noinspection ForgottenDebugOutputInspection */
\xdebug_start_function_monitor(ResourceOperations::getFunctions());
}
Timer::start();
try {
if (!$test instanceof WarningTestCase &&
$this->enforceTimeLimit &&
($this->defaultTimeLimit || $test->getSize() != \PHPUnit\Util\Test::UNKNOWN) &&
\extension_loaded('pcntl') && \class_exists(Invoker::class)) {
switch ($test->getSize()) {
case \PHPUnit\Util\Test::SMALL:
$_timeout = $this->timeoutForSmallTests;
break;
case \PHPUnit\Util\Test::MEDIUM:
$_timeout = $this->timeoutForMediumTests;
break;
case \PHPUnit\Util\Test::LARGE:
$_timeout = $this->timeoutForLargeTests;
break;
case \PHPUnit\Util\Test::UNKNOWN:
$_timeout = $this->defaultTimeLimit;
break;
}
$invoker = new Invoker;
$invoker->invoke([$test, 'runBare'], [], $_timeout);
} else {
$test->runBare();
}
} catch (TimeoutException $e) {
$this->addFailure(
$test,
new RiskyTestError(
$e->getMessage()
),
$_timeout
);
$risky = true;
} catch (MockObjectException $e) {
$e = new Warning(
$e->getMessage()
);
$warning = true;
} catch (AssertionFailedError $e) {
$failure = true;
if ($e instanceof RiskyTestError) {
$risky = true;
} elseif ($e instanceof IncompleteTestError) {
$incomplete = true;
} elseif ($e instanceof SkippedTestError) {
$skipped = true;
}
} catch (AssertionError $e) {
$test->addToAssertionCount(1);
$failure = true;
$frame = $e->getTrace()[0];
$e = new AssertionFailedError(
\sprintf(
'%s in %s:%s',
$e->getMessage(),
$frame['file'],
$frame['line']
)
);
} catch (Warning $e) {
$warning = true;
} catch (Exception $e) {
$error = true;
} catch (Throwable $e) {
$e = new ExceptionWrapper($e);
$error = true;
}
$time = Timer::stop();
$test->addToAssertionCount(Assert::getCount());
if ($monitorFunctions) {
$blacklist = new Blacklist;
/** @noinspection ForgottenDebugOutputInspection */
$functions = \xdebug_get_monitored_functions();
/* @noinspection ForgottenDebugOutputInspection */
\xdebug_stop_function_monitor();
foreach ($functions as $function) {
if (!$blacklist->isBlacklisted($function['filename'])) {
$this->addFailure(
$test,
new RiskyTestError(
\sprintf(
'%s() used in %s:%s',
$function['function'],
$function['filename'],
$function['lineno']
)
),
$time
);
}
}
}
if ($this->beStrictAboutTestsThatDoNotTestAnything &&
$test->getNumAssertions() == 0) {
$risky = true;
}
if ($collectCodeCoverage) {
$append = !$risky && !$incomplete && !$skipped;
$linesToBeCovered = [];
$linesToBeUsed = [];
if ($append && $test instanceof TestCase) {
try {
$linesToBeCovered = \PHPUnit\Util\Test::getLinesToBeCovered(
\get_class($test),
$test->getName(false)
);
$linesToBeUsed = \PHPUnit\Util\Test::getLinesToBeUsed(
\get_class($test),
$test->getName(false)
);
} catch (InvalidCoversTargetException $cce) {
$this->addWarning(
$test,
new Warning(
$cce->getMessage()
),
$time
);
}
}
try {
$this->codeCoverage->stop(
$append,
$linesToBeCovered,
$linesToBeUsed
);
} catch (UnintentionallyCoveredCodeException $cce) {
$this->addFailure(
$test,
new UnintentionallyCoveredCodeError(
'This test executed code that is not listed as code to be covered or used:' .
\PHP_EOL . $cce->getMessage()
),
$time
);
} catch (OriginalCoveredCodeNotExecutedException $cce) {
$this->addFailure(
$test,
new CoveredCodeNotExecutedException(
'This test did not execute all the code that is listed as code to be covered:' .
\PHP_EOL . $cce->getMessage()
),
$time
);
} catch (OriginalMissingCoversAnnotationException $cce) {
if ($linesToBeCovered !== false) {
$this->addFailure(
$test,
new MissingCoversAnnotationException(
'This test does not have a @covers annotation but is expected to have one'
),
$time
);
}
} catch (OriginalCodeCoverageException $cce) {
$error = true;
$e = $e ?? $cce;
}
}
if (isset($errorHandler)) {
$errorHandler->unregister();
unset($errorHandler);
}
if ($error) {
$this->addError($test, $e, $time);
} elseif ($failure) {
$this->addFailure($test, $e, $time);
} elseif ($warning) {
$this->addWarning($test, $e, $time);
} elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
!$test->doesNotPerformAssertions() &&
$test->getNumAssertions() == 0) {
try {
$reflected = new \ReflectionClass($test);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$name = $test->getName(false);
if ($name && $reflected->hasMethod($name)) {
try {
$reflected = $reflected->getMethod($name);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
}
$this->addFailure(
$test,
new RiskyTestError(
\sprintf(
"This test did not perform any assertions\n\n%s:%d",
$reflected->getFileName(),
$reflected->getStartLine()
)
),
$time
);
} elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
$test->doesNotPerformAssertions() &&
$test->getNumAssertions() > 0) {
$this->addFailure(
$test,
new RiskyTestError(
\sprintf(
'This test is annotated with "@doesNotPerformAssertions" but performed %d assertions',
$test->getNumAssertions()
)
),
$time
);
} elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) {
$this->addFailure(
$test,
new OutputError(
\sprintf(
'This test printed output: %s',
$test->getActualOutput()
)
),
$time
);
} elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof TestCase) {
$annotations = $test->getAnnotations();
if (isset($annotations['method']['todo'])) {
$this->addFailure(
$test,
new RiskyTestError(
'Test method is annotated with @todo'
),
$time
);
}
}
$this->endTest($test, $time);
}
/**
* Gets the number of run tests.
*/
public function count(): int
{
return $this->runTests;
}
/**
* Checks whether the test run should stop.
*/
public function shouldStop(): bool
{
return $this->stop;
}
/**
* Marks that the test run should stop.
*/
public function stop(): void
{
$this->stop = true;
}
/**
* Returns the code coverage object.
*/
public function getCodeCoverage(): ?CodeCoverage
{
return $this->codeCoverage;
}
/**
* Sets the code coverage object.
*/
public function setCodeCoverage(CodeCoverage $codeCoverage): void
{
$this->codeCoverage = $codeCoverage;
}
/**
* Enables or disables the deprecation-to-exception conversion.
*/
public function convertDeprecationsToExceptions(bool $flag): void
{
$this->convertDeprecationsToExceptions = $flag;
}
/**
* Returns the deprecation-to-exception conversion setting.
*/
public function getConvertDeprecationsToExceptions(): bool
{
return $this->convertDeprecationsToExceptions;
}
/**
* Enables or disables the error-to-exception conversion.
*/
public function convertErrorsToExceptions(bool $flag): void
{
$this->convertErrorsToExceptions = $flag;
}
/**
* Returns the error-to-exception conversion setting.
*/
public function getConvertErrorsToExceptions(): bool
{
return $this->convertErrorsToExceptions;
}
/**
* Enables or disables the notice-to-exception conversion.
*/
public function convertNoticesToExceptions(bool $flag): void
{
$this->convertNoticesToExceptions = $flag;
}
/**
* Returns the notice-to-exception conversion setting.
*/
public function getConvertNoticesToExceptions(): bool
{
return $this->convertNoticesToExceptions;
}
/**
* Enables or disables the warning-to-exception conversion.
*/
public function convertWarningsToExceptions(bool $flag): void
{
$this->convertWarningsToExceptions = $flag;
}
/**
* Returns the warning-to-exception conversion setting.
*/
public function getConvertWarningsToExceptions(): bool
{
return $this->convertWarningsToExceptions;
}
/**
* Enables or disables the stopping when an error occurs.
*/
public function stopOnError(bool $flag): void
{
$this->stopOnError = $flag;
}
/**
* Enables or disables the stopping when a failure occurs.
*/
public function stopOnFailure(bool $flag): void
{
$this->stopOnFailure = $flag;
}
/**
* Enables or disables the stopping when a warning occurs.
*/
public function stopOnWarning(bool $flag): void
{
$this->stopOnWarning = $flag;
}
public function beStrictAboutTestsThatDoNotTestAnything(bool $flag): void
{
$this->beStrictAboutTestsThatDoNotTestAnything = $flag;
}
public function isStrictAboutTestsThatDoNotTestAnything(): bool
{
return $this->beStrictAboutTestsThatDoNotTestAnything;
}
public function beStrictAboutOutputDuringTests(bool $flag): void
{
$this->beStrictAboutOutputDuringTests = $flag;
}
public function isStrictAboutOutputDuringTests(): bool
{
return $this->beStrictAboutOutputDuringTests;
}
public function beStrictAboutResourceUsageDuringSmallTests(bool $flag): void
{
$this->beStrictAboutResourceUsageDuringSmallTests = $flag;
}
public function isStrictAboutResourceUsageDuringSmallTests(): bool
{
return $this->beStrictAboutResourceUsageDuringSmallTests;
}
public function enforceTimeLimit(bool $flag): void
{
$this->enforceTimeLimit = $flag;
}
public function enforcesTimeLimit(): bool
{
return $this->enforceTimeLimit;
}
public function beStrictAboutTodoAnnotatedTests(bool $flag): void
{
$this->beStrictAboutTodoAnnotatedTests = $flag;
}
public function isStrictAboutTodoAnnotatedTests(): bool
{
return $this->beStrictAboutTodoAnnotatedTests;
}
/**
* Enables or disables the stopping for risky tests.
*/
public function stopOnRisky(bool $flag): void
{
$this->stopOnRisky = $flag;
}
/**
* Enables or disables the stopping for incomplete tests.
*/
public function stopOnIncomplete(bool $flag): void
{
$this->stopOnIncomplete = $flag;
}
/**
* Enables or disables the stopping for skipped tests.
*/
public function stopOnSkipped(bool $flag): void
{
$this->stopOnSkipped = $flag;
}
/**
* Enables or disables the stopping for defects: error, failure, warning
*/
public function stopOnDefect(bool $flag): void
{
$this->stopOnDefect = $flag;
}
/**
* Returns the time spent running the tests.
*/
public function time(): float
{
return $this->time;
}
/**
* Returns whether the entire test was successful or not.
*/
public function wasSuccessful(): bool
{
return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings);
}
public function wasSuccessfulIgnoringWarnings(): bool
{
return empty($this->errors) && empty($this->failures);
}
/**
* Sets the default timeout for tests
*/
public function setDefaultTimeLimit(int $timeout): void
{
$this->defaultTimeLimit = $timeout;
}
/**
* Sets the timeout for small tests.
*/
public function setTimeoutForSmallTests(int $timeout): void
{
$this->timeoutForSmallTests = $timeout;
}
/**
* Sets the timeout for medium tests.
*/
public function setTimeoutForMediumTests(int $timeout): void
{
$this->timeoutForMediumTests = $timeout;
}
/**
* Sets the timeout for large tests.
*/
public function setTimeoutForLargeTests(int $timeout): void
{
$this->timeoutForLargeTests = $timeout;
}
/**
* Returns the set timeout for large tests.
*/
public function getTimeoutForLargeTests(): int
{
return $this->timeoutForLargeTests;
}
public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
{
$this->registerMockObjectsFromTestArgumentsRecursively = $flag;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class SkippedTestCase extends TestCase
{
/**
* @var bool
*/
protected $backupGlobals = false;
/**
* @var bool
*/
protected $backupStaticAttributes = false;
/**
* @var bool
*/
protected $runTestInSeparateProcess = false;
/**
* @var bool
*/
protected $useErrorHandler = false;
/**
* @var string
*/
private $message;
public function __construct(string $className, string $methodName, string $message = '')
{
parent::__construct($className . '::' . $methodName);
$this->message = $message;
}
public function getMessage(): string
{
return $this->message;
}
/**
* Returns a string representation of the test case.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
return $this->getName();
}
/**
* @throws Exception
*/
protected function runTest(): void
{
$this->markTestSkipped($this->message);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use PHPUnit\Util\Test as TestUtil;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TestBuilder
{
public function build(\ReflectionClass $theClass, string $methodName): Test
{
$className = $theClass->getName();
if (!$theClass->isInstantiable()) {
return new WarningTestCase(
\sprintf('Cannot instantiate class "%s".', $className)
);
}
$backupSettings = TestUtil::getBackupSettings(
$className,
$methodName
);
$preserveGlobalState = TestUtil::getPreserveGlobalStateSettings(
$className,
$methodName
);
$runTestInSeparateProcess = TestUtil::getProcessIsolationSettings(
$className,
$methodName
);
$runClassInSeparateProcess = TestUtil::getClassProcessIsolationSettings(
$className,
$methodName
);
$constructor = $theClass->getConstructor();
if ($constructor === null) {
throw new Exception('No valid test provided.');
}
$parameters = $constructor->getParameters();
// TestCase() or TestCase($name)
if (\count($parameters) < 2) {
$test = $this->buildTestWithoutData($className);
} // TestCase($name, $data)
else {
try {
$data = TestUtil::getProvidedData(
$className,
$methodName
);
} catch (IncompleteTestError $e) {
$message = \sprintf(
'Test for %s::%s marked incomplete by data provider',
$className,
$methodName
);
$message = $this->appendExceptionMessageIfAvailable($e, $message);
$data = new IncompleteTestCase($className, $methodName, $message);
} catch (SkippedTestError $e) {
$message = \sprintf(
'Test for %s::%s skipped by data provider',
$className,
$methodName
);
$message = $this->appendExceptionMessageIfAvailable($e, $message);
$data = new SkippedTestCase($className, $methodName, $message);
} catch (\Throwable $t) {
$message = \sprintf(
'The data provider specified for %s::%s is invalid.',
$className,
$methodName
);
$message = $this->appendExceptionMessageIfAvailable($t, $message);
$data = new WarningTestCase($message);
}
// Test method with @dataProvider.
if (isset($data)) {
$test = $this->buildDataProviderTestSuite(
$methodName,
$className,
$data,
$runTestInSeparateProcess,
$preserveGlobalState,
$runClassInSeparateProcess,
$backupSettings
);
} else {
$test = $this->buildTestWithoutData($className);
}
}
if ($test instanceof TestCase) {
$test->setName($methodName);
$this->configureTestCase(
$test,
$runTestInSeparateProcess,
$preserveGlobalState,
$runClassInSeparateProcess,
$backupSettings
);
}
return $test;
}
private function appendExceptionMessageIfAvailable(\Throwable $e, string $message): string
{
$_message = $e->getMessage();
if (!empty($_message)) {
$message .= "\n" . $_message;
}
return $message;
}
/** @psalm-param class-string $className */
private function buildTestWithoutData(string $className)
{
return new $className;
}
/** @psalm-param class-string $className */
private function buildDataProviderTestSuite(
string $methodName,
string $className,
$data,
bool $runTestInSeparateProcess,
?bool $preserveGlobalState,
bool $runClassInSeparateProcess,
array $backupSettings
): DataProviderTestSuite {
$dataProviderTestSuite = new DataProviderTestSuite(
$className . '::' . $methodName
);
$groups = TestUtil::getGroups($className, $methodName);
if ($data instanceof WarningTestCase ||
$data instanceof SkippedTestCase ||
$data instanceof IncompleteTestCase) {
$dataProviderTestSuite->addTest($data, $groups);
} else {
foreach ($data as $_dataName => $_data) {
$_test = new $className($methodName, $_data, $_dataName);
\assert($_test instanceof TestCase);
$this->configureTestCase(
$_test,
$runTestInSeparateProcess,
$preserveGlobalState,
$runClassInSeparateProcess,
$backupSettings
);
$dataProviderTestSuite->addTest($_test, $groups);
}
}
return $dataProviderTestSuite;
}
private function configureTestCase(
TestCase $test,
bool $runTestInSeparateProcess,
?bool $preserveGlobalState,
bool $runClassInSeparateProcess,
array $backupSettings
): void {
if ($runTestInSeparateProcess) {
$test->setRunTestInSeparateProcess(true);
if ($preserveGlobalState !== null) {
$test->setPreserveGlobalState($preserveGlobalState);
}
}
if ($runClassInSeparateProcess) {
$test->setRunClassInSeparateProcess(true);
if ($preserveGlobalState !== null) {
$test->setPreserveGlobalState($preserveGlobalState);
}
}
if ($backupSettings['backupGlobals'] !== null) {
$test->setBackupGlobals($backupSettings['backupGlobals']);
}
if ($backupSettings['backupStaticAttributes'] !== null) {
$test->setBackupStaticAttributes(
$backupSettings['backupStaticAttributes']
);
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use ArrayAccess;
use Countable;
use DOMDocument;
use DOMElement;
use PHPUnit\Framework\Constraint\ArrayHasKey;
use PHPUnit\Framework\Constraint\ArraySubset;
use PHPUnit\Framework\Constraint\Attribute;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\Constraint\ClassHasAttribute;
use PHPUnit\Framework\Constraint\ClassHasStaticAttribute;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Constraint\Count;
use PHPUnit\Framework\Constraint\DirectoryExists;
use PHPUnit\Framework\Constraint\FileExists;
use PHPUnit\Framework\Constraint\GreaterThan;
use PHPUnit\Framework\Constraint\IsAnything;
use PHPUnit\Framework\Constraint\IsEmpty;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\Constraint\IsFalse;
use PHPUnit\Framework\Constraint\IsFinite;
use PHPUnit\Framework\Constraint\IsIdentical;
use PHPUnit\Framework\Constraint\IsInfinite;
use PHPUnit\Framework\Constraint\IsInstanceOf;
use PHPUnit\Framework\Constraint\IsJson;
use PHPUnit\Framework\Constraint\IsNan;
use PHPUnit\Framework\Constraint\IsNull;
use PHPUnit\Framework\Constraint\IsReadable;
use PHPUnit\Framework\Constraint\IsTrue;
use PHPUnit\Framework\Constraint\IsType;
use PHPUnit\Framework\Constraint\IsWritable;
use PHPUnit\Framework\Constraint\JsonMatches;
use PHPUnit\Framework\Constraint\LessThan;
use PHPUnit\Framework\Constraint\LogicalAnd;
use PHPUnit\Framework\Constraint\LogicalNot;
use PHPUnit\Framework\Constraint\LogicalOr;
use PHPUnit\Framework\Constraint\LogicalXor;
use PHPUnit\Framework\Constraint\ObjectHasAttribute;
use PHPUnit\Framework\Constraint\RegularExpression;
use PHPUnit\Framework\Constraint\SameSize;
use PHPUnit\Framework\Constraint\StringContains;
use PHPUnit\Framework\Constraint\StringEndsWith;
use PHPUnit\Framework\Constraint\StringMatchesFormatDescription;
use PHPUnit\Framework\Constraint\StringStartsWith;
use PHPUnit\Framework\Constraint\TraversableContains;
use PHPUnit\Framework\Constraint\TraversableContainsOnly;
use PHPUnit\Util\Type;
use PHPUnit\Util\Xml;
use ReflectionClass;
use ReflectionObject;
use Traversable;
/**
* A set of assertion methods.
*/
abstract class Assert
{
/**
* @var int
*/
private static $count = 0;
/**
* Asserts that an array has a specified key.
*
* @param int|string $key
* @param array|ArrayAccess $array
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertArrayHasKey($key, $array, string $message = ''): void
{
if (!(\is_int($key) || \is_string($key))) {
throw InvalidArgumentException::create(
1,
'integer or string'
);
}
if (!(\is_array($array) || $array instanceof ArrayAccess)) {
throw InvalidArgumentException::create(
2,
'array or ArrayAccess'
);
}
$constraint = new ArrayHasKey($key);
static::assertThat($array, $constraint, $message);
}
/**
* Asserts that an array has a specified subset.
*
* @param array|ArrayAccess $subset
* @param array|ArrayAccess $array
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @codeCoverageIgnore
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494
*/
public static function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void
{
self::createWarning('assertArraySubset() is deprecated and will be removed in PHPUnit 9.');
if (!(\is_array($subset) || $subset instanceof ArrayAccess)) {
throw InvalidArgumentException::create(
1,
'array or ArrayAccess'
);
}
if (!(\is_array($array) || $array instanceof ArrayAccess)) {
throw InvalidArgumentException::create(
2,
'array or ArrayAccess'
);
}
$constraint = new ArraySubset($subset, $checkForObjectIdentity);
static::assertThat($array, $constraint, $message);
}
/**
* Asserts that an array does not have a specified key.
*
* @param int|string $key
* @param array|ArrayAccess $array
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertArrayNotHasKey($key, $array, string $message = ''): void
{
if (!(\is_int($key) || \is_string($key))) {
throw InvalidArgumentException::create(
1,
'integer or string'
);
}
if (!(\is_array($array) || $array instanceof ArrayAccess)) {
throw InvalidArgumentException::create(
2,
'array or ArrayAccess'
);
}
$constraint = new LogicalNot(
new ArrayHasKey($key)
);
static::assertThat($array, $constraint, $message);
}
/**
* Asserts that a haystack contains a needle.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
{
// @codeCoverageIgnoreStart
if (\is_string($haystack)) {
self::createWarning('Using assertContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringContainsString() or assertStringContainsStringIgnoringCase() instead.');
}
if (!$checkForObjectIdentity) {
self::createWarning('The optional $checkForObjectIdentity parameter of assertContains() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertContainsEquals() instead.');
}
if ($checkForNonObjectIdentity) {
self::createWarning('The optional $checkForNonObjectIdentity parameter of assertContains() is deprecated and will be removed in PHPUnit 9.');
}
if ($ignoreCase) {
self::createWarning('The optional $ignoreCase parameter of assertContains() is deprecated and will be removed in PHPUnit 9.');
}
// @codeCoverageIgnoreEnd
if (\is_array($haystack) ||
(\is_object($haystack) && $haystack instanceof Traversable)) {
$constraint = new TraversableContains(
$needle,
$checkForObjectIdentity,
$checkForNonObjectIdentity
);
} elseif (\is_string($haystack)) {
if (!\is_string($needle)) {
throw InvalidArgumentException::create(
1,
'string'
);
}
$constraint = new StringContains(
$needle,
$ignoreCase
);
} else {
throw InvalidArgumentException::create(
2,
'array, traversable or string'
);
}
static::assertThat($haystack, $constraint, $message);
}
public static function assertContainsEquals($needle, iterable $haystack, string $message = ''): void
{
$constraint = new TraversableContains($needle, false, false);
static::assertThat($haystack, $constraint, $message);
}
/**
* Asserts that a haystack that is stored in a static attribute of a class
* or an attribute of an object contains a needle.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
{
self::createWarning('assertAttributeContains() is deprecated and will be removed in PHPUnit 9.');
static::assertContains(
$needle,
static::readAttribute($haystackClassOrObject, $haystackAttributeName),
$message,
$ignoreCase,
$checkForObjectIdentity,
$checkForNonObjectIdentity
);
}
/**
* Asserts that a haystack does not contain a needle.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertNotContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
{
// @codeCoverageIgnoreStart
if (\is_string($haystack)) {
self::createWarning('Using assertNotContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringNotContainsString() or assertStringNotContainsStringIgnoringCase() instead.');
}
if (!$checkForObjectIdentity) {
self::createWarning('The optional $checkForObjectIdentity parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertNotContainsEquals() instead.');
}
if ($checkForNonObjectIdentity) {
self::createWarning('The optional $checkForNonObjectIdentity parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9.');
}
if ($ignoreCase) {
self::createWarning('The optional $ignoreCase parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9.');
}
// @codeCoverageIgnoreEnd
if (\is_array($haystack) ||
(\is_object($haystack) && $haystack instanceof Traversable)) {
$constraint = new LogicalNot(
new TraversableContains(
$needle,
$checkForObjectIdentity,
$checkForNonObjectIdentity
)
);
} elseif (\is_string($haystack)) {
if (!\is_string($needle)) {
throw InvalidArgumentException::create(
1,
'string'
);
}
$constraint = new LogicalNot(
new StringContains(
$needle,
$ignoreCase
)
);
} else {
throw InvalidArgumentException::create(
2,
'array, traversable or string'
);
}
static::assertThat($haystack, $constraint, $message);
}
public static function assertNotContainsEquals($needle, iterable $haystack, string $message = ''): void
{
$constraint = new LogicalNot(new TraversableContains($needle, false, false));
static::assertThat($haystack, $constraint, $message);
}
/**
* Asserts that a haystack that is stored in a static attribute of a class
* or an attribute of an object does not contain a needle.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeNotContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
{
self::createWarning('assertAttributeNotContains() is deprecated and will be removed in PHPUnit 9.');
static::assertNotContains(
$needle,
static::readAttribute($haystackClassOrObject, $haystackAttributeName),
$message,
$ignoreCase,
$checkForObjectIdentity,
$checkForNonObjectIdentity
);
}
/**
* Asserts that a haystack contains only values of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
{
if ($isNativeType === null) {
$isNativeType = Type::isType($type);
}
static::assertThat(
$haystack,
new TraversableContainsOnly(
$type,
$isNativeType
),
$message
);
}
/**
* Asserts that a haystack contains only instances of a given class name.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void
{
static::assertThat(
$haystack,
new TraversableContainsOnly(
$className,
false
),
$message
);
}
/**
* Asserts that a haystack that is stored in a static attribute of a class
* or an attribute of an object contains only values of a given type.
*
* @param object|string $haystackClassOrObject
* @param bool $isNativeType
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void
{
self::createWarning('assertAttributeContainsOnly() is deprecated and will be removed in PHPUnit 9.');
static::assertContainsOnly(
$type,
static::readAttribute($haystackClassOrObject, $haystackAttributeName),
$isNativeType,
$message
);
}
/**
* Asserts that a haystack does not contain only values of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
{
if ($isNativeType === null) {
$isNativeType = Type::isType($type);
}
static::assertThat(
$haystack,
new LogicalNot(
new TraversableContainsOnly(
$type,
$isNativeType
)
),
$message
);
}
/**
* Asserts that a haystack that is stored in a static attribute of a class
* or an attribute of an object does not contain only values of a given
* type.
*
* @param object|string $haystackClassOrObject
* @param bool $isNativeType
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeNotContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void
{
self::createWarning('assertAttributeNotContainsOnly() is deprecated and will be removed in PHPUnit 9.');
static::assertNotContainsOnly(
$type,
static::readAttribute($haystackClassOrObject, $haystackAttributeName),
$isNativeType,
$message
);
}
/**
* Asserts the number of elements of an array, Countable or Traversable.
*
* @param Countable|iterable $haystack
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertCount(int $expectedCount, $haystack, string $message = ''): void
{
if (!$haystack instanceof Countable && !\is_iterable($haystack)) {
throw InvalidArgumentException::create(2, 'countable or iterable');
}
static::assertThat(
$haystack,
new Count($expectedCount),
$message
);
}
/**
* Asserts the number of elements of an array, Countable or Traversable
* that is stored in an attribute.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeCount() is deprecated and will be removed in PHPUnit 9.');
static::assertCount(
$expectedCount,
static::readAttribute($haystackClassOrObject, $haystackAttributeName),
$message
);
}
/**
* Asserts the number of elements of an array, Countable or Traversable.
*
* @param Countable|iterable $haystack
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertNotCount(int $expectedCount, $haystack, string $message = ''): void
{
if (!$haystack instanceof Countable && !\is_iterable($haystack)) {
throw InvalidArgumentException::create(2, 'countable or iterable');
}
$constraint = new LogicalNot(
new Count($expectedCount)
);
static::assertThat($haystack, $constraint, $message);
}
/**
* Asserts the number of elements of an array, Countable or Traversable
* that is stored in an attribute.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeNotCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeNotCount() is deprecated and will be removed in PHPUnit 9.');
static::assertNotCount(
$expectedCount,
static::readAttribute($haystackClassOrObject, $haystackAttributeName),
$message
);
}
/**
* Asserts that two variables are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertEquals($expected, $actual, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
{
// @codeCoverageIgnoreStart
if ($delta !== 0.0) {
self::createWarning('The optional $delta parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsWithDelta() instead.');
}
if ($maxDepth !== 10) {
self::createWarning('The optional $maxDepth parameter of assertEquals() is deprecated and will be removed in PHPUnit 9.');
}
if ($canonicalize) {
self::createWarning('The optional $canonicalize parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsCanonicalizing() instead.');
}
if ($ignoreCase) {
self::createWarning('The optional $ignoreCase parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsIgnoringCase() instead.');
}
// @codeCoverageIgnoreEnd
$constraint = new IsEqual(
$expected,
$delta,
$maxDepth,
$canonicalize,
$ignoreCase
);
static::assertThat($actual, $constraint, $message);
}
/**
* Asserts that two variables are equal (canonicalizing).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertEqualsCanonicalizing($expected, $actual, string $message = ''): void
{
$constraint = new IsEqual(
$expected,
0.0,
10,
true,
false
);
static::assertThat($actual, $constraint, $message);
}
/**
* Asserts that two variables are equal (ignoring case).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertEqualsIgnoringCase($expected, $actual, string $message = ''): void
{
$constraint = new IsEqual(
$expected,
0.0,
10,
false,
true
);
static::assertThat($actual, $constraint, $message);
}
/**
* Asserts that two variables are equal (with delta).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
{
$constraint = new IsEqual(
$expected,
$delta
);
static::assertThat($actual, $constraint, $message);
}
/**
* Asserts that a variable is equal to an attribute of an object.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
{
self::createWarning('assertAttributeEquals() is deprecated and will be removed in PHPUnit 9.');
static::assertEquals(
$expected,
static::readAttribute($actualClassOrObject, $actualAttributeName),
$message,
$delta,
$maxDepth,
$canonicalize,
$ignoreCase
);
}
/**
* Asserts that two variables are not equal.
*
* @param float $delta
* @param int $maxDepth
* @param bool $canonicalize
* @param bool $ignoreCase
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotEquals($expected, $actual, string $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false): void
{
// @codeCoverageIgnoreStart
if ($delta !== 0.0) {
self::createWarning('The optional $delta parameter of assertNotEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertNotEqualsWithDelta() instead.');
}
if ($maxDepth !== 10) {
self::createWarning('The optional $maxDepth parameter of assertNotEquals() is deprecated and will be removed in PHPUnit 9.');
}
if ($canonicalize) {
self::createWarning('The optional $canonicalize parameter of assertNotEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertNotEqualsCanonicalizing() instead.');
}
if ($ignoreCase) {
self::createWarning('The optional $ignoreCase parameter of assertNotEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertNotEqualsIgnoringCase() instead.');
}
// @codeCoverageIgnoreEnd
$constraint = new LogicalNot(
new IsEqual(
$expected,
$delta,
$maxDepth,
$canonicalize,
$ignoreCase
)
);
static::assertThat($actual, $constraint, $message);
}
/**
* Asserts that two variables are not equal (canonicalizing).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotEqualsCanonicalizing($expected, $actual, string $message = ''): void
{
$constraint = new LogicalNot(
new IsEqual(
$expected,
0.0,
10,
true,
false
)
);
static::assertThat($actual, $constraint, $message);
}
/**
* Asserts that two variables are not equal (ignoring case).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotEqualsIgnoringCase($expected, $actual, string $message = ''): void
{
$constraint = new LogicalNot(
new IsEqual(
$expected,
0.0,
10,
false,
true
)
);
static::assertThat($actual, $constraint, $message);
}
/**
* Asserts that two variables are not equal (with delta).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
{
$constraint = new LogicalNot(
new IsEqual(
$expected,
$delta
)
);
static::assertThat($actual, $constraint, $message);
}
/**
* Asserts that a variable is not equal to an attribute of an object.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeNotEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
{
self::createWarning('assertAttributeNotEquals() is deprecated and will be removed in PHPUnit 9.');
static::assertNotEquals(
$expected,
static::readAttribute($actualClassOrObject, $actualAttributeName),
$message,
$delta,
$maxDepth,
$canonicalize,
$ignoreCase
);
}
/**
* Asserts that a variable is empty.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert empty $actual
*/
public static function assertEmpty($actual, string $message = ''): void
{
static::assertThat($actual, static::isEmpty(), $message);
}
/**
* Asserts that a static attribute of a class or an attribute of an object
* is empty.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeEmpty() is deprecated and will be removed in PHPUnit 9.');
static::assertEmpty(
static::readAttribute($haystackClassOrObject, $haystackAttributeName),
$message
);
}
/**
* Asserts that a variable is not empty.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !empty $actual
*/
public static function assertNotEmpty($actual, string $message = ''): void
{
static::assertThat($actual, static::logicalNot(static::isEmpty()), $message);
}
/**
* Asserts that a static attribute of a class or an attribute of an object
* is not empty.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeNotEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeNotEmpty() is deprecated and will be removed in PHPUnit 9.');
static::assertNotEmpty(
static::readAttribute($haystackClassOrObject, $haystackAttributeName),
$message
);
}
/**
* Asserts that a value is greater than another value.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertGreaterThan($expected, $actual, string $message = ''): void
{
static::assertThat($actual, static::greaterThan($expected), $message);
}
/**
* Asserts that an attribute is greater than another value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeGreaterThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeGreaterThan() is deprecated and will be removed in PHPUnit 9.');
static::assertGreaterThan(
$expected,
static::readAttribute($actualClassOrObject, $actualAttributeName),
$message
);
}
/**
* Asserts that a value is greater than or equal to another value.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void
{
static::assertThat(
$actual,
static::greaterThanOrEqual($expected),
$message
);
}
/**
* Asserts that an attribute is greater than or equal to another value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeGreaterThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeGreaterThanOrEqual() is deprecated and will be removed in PHPUnit 9.');
static::assertGreaterThanOrEqual(
$expected,
static::readAttribute($actualClassOrObject, $actualAttributeName),
$message
);
}
/**
* Asserts that a value is smaller than another value.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertLessThan($expected, $actual, string $message = ''): void
{
static::assertThat($actual, static::lessThan($expected), $message);
}
/**
* Asserts that an attribute is smaller than another value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeLessThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeLessThan() is deprecated and will be removed in PHPUnit 9.');
static::assertLessThan(
$expected,
static::readAttribute($actualClassOrObject, $actualAttributeName),
$message
);
}
/**
* Asserts that a value is smaller than or equal to another value.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertLessThanOrEqual($expected, $actual, string $message = ''): void
{
static::assertThat($actual, static::lessThanOrEqual($expected), $message);
}
/**
* Asserts that an attribute is smaller than or equal to another value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeLessThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeLessThanOrEqual() is deprecated and will be removed in PHPUnit 9.');
static::assertLessThanOrEqual(
$expected,
static::readAttribute($actualClassOrObject, $actualAttributeName),
$message
);
}
/**
* Asserts that the contents of one file is equal to the contents of another
* file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFileEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
{
static::assertFileExists($expected, $message);
static::assertFileExists($actual, $message);
$constraint = new IsEqual(
\file_get_contents($expected),
0.0,
10,
$canonicalize,
$ignoreCase
);
static::assertThat(\file_get_contents($actual), $constraint, $message);
}
/**
* Asserts that the contents of one file is not equal to the contents of
* another file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFileNotEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
{
static::assertFileExists($expected, $message);
static::assertFileExists($actual, $message);
$constraint = new LogicalNot(
new IsEqual(
\file_get_contents($expected),
0.0,
10,
$canonicalize,
$ignoreCase
)
);
static::assertThat(\file_get_contents($actual), $constraint, $message);
}
/**
* Asserts that the contents of a string is equal
* to the contents of a file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
{
static::assertFileExists($expectedFile, $message);
$constraint = new IsEqual(
\file_get_contents($expectedFile),
0.0,
10,
$canonicalize,
$ignoreCase
);
static::assertThat($actualString, $constraint, $message);
}
/**
* Asserts that the contents of a string is not equal
* to the contents of a file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
{
static::assertFileExists($expectedFile, $message);
$constraint = new LogicalNot(
new IsEqual(
\file_get_contents($expectedFile),
0.0,
10,
$canonicalize,
$ignoreCase
)
);
static::assertThat($actualString, $constraint, $message);
}
/**
* Asserts that a file/dir is readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertIsReadable(string $filename, string $message = ''): void
{
static::assertThat($filename, new IsReadable, $message);
}
/**
* Asserts that a file/dir exists and is not readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotIsReadable(string $filename, string $message = ''): void
{
static::assertThat($filename, new LogicalNot(new IsReadable), $message);
}
/**
* Asserts that a file/dir exists and is writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertIsWritable(string $filename, string $message = ''): void
{
static::assertThat($filename, new IsWritable, $message);
}
/**
* Asserts that a file/dir exists and is not writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotIsWritable(string $filename, string $message = ''): void
{
static::assertThat($filename, new LogicalNot(new IsWritable), $message);
}
/**
* Asserts that a directory exists.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertDirectoryExists(string $directory, string $message = ''): void
{
static::assertThat($directory, new DirectoryExists, $message);
}
/**
* Asserts that a directory does not exist.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertDirectoryNotExists(string $directory, string $message = ''): void
{
static::assertThat($directory, new LogicalNot(new DirectoryExists), $message);
}
/**
* Asserts that a directory exists and is readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertDirectoryIsReadable(string $directory, string $message = ''): void
{
self::assertDirectoryExists($directory, $message);
self::assertIsReadable($directory, $message);
}
/**
* Asserts that a directory exists and is not readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertDirectoryNotIsReadable(string $directory, string $message = ''): void
{
self::assertDirectoryExists($directory, $message);
self::assertNotIsReadable($directory, $message);
}
/**
* Asserts that a directory exists and is writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertDirectoryIsWritable(string $directory, string $message = ''): void
{
self::assertDirectoryExists($directory, $message);
self::assertIsWritable($directory, $message);
}
/**
* Asserts that a directory exists and is not writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertDirectoryNotIsWritable(string $directory, string $message = ''): void
{
self::assertDirectoryExists($directory, $message);
self::assertNotIsWritable($directory, $message);
}
/**
* Asserts that a file exists.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFileExists(string $filename, string $message = ''): void
{
static::assertThat($filename, new FileExists, $message);
}
/**
* Asserts that a file does not exist.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFileNotExists(string $filename, string $message = ''): void
{
static::assertThat($filename, new LogicalNot(new FileExists), $message);
}
/**
* Asserts that a file exists and is readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFileIsReadable(string $file, string $message = ''): void
{
self::assertFileExists($file, $message);
self::assertIsReadable($file, $message);
}
/**
* Asserts that a file exists and is not readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFileNotIsReadable(string $file, string $message = ''): void
{
self::assertFileExists($file, $message);
self::assertNotIsReadable($file, $message);
}
/**
* Asserts that a file exists and is writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFileIsWritable(string $file, string $message = ''): void
{
self::assertFileExists($file, $message);
self::assertIsWritable($file, $message);
}
/**
* Asserts that a file exists and is not writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFileNotIsWritable(string $file, string $message = ''): void
{
self::assertFileExists($file, $message);
self::assertNotIsWritable($file, $message);
}
/**
* Asserts that a condition is true.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert true $condition
*/
public static function assertTrue($condition, string $message = ''): void
{
static::assertThat($condition, static::isTrue(), $message);
}
/**
* Asserts that a condition is not true.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !true $condition
*/
public static function assertNotTrue($condition, string $message = ''): void
{
static::assertThat($condition, static::logicalNot(static::isTrue()), $message);
}
/**
* Asserts that a condition is false.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert false $condition
*/
public static function assertFalse($condition, string $message = ''): void
{
static::assertThat($condition, static::isFalse(), $message);
}
/**
* Asserts that a condition is not false.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !false $condition
*/
public static function assertNotFalse($condition, string $message = ''): void
{
static::assertThat($condition, static::logicalNot(static::isFalse()), $message);
}
/**
* Asserts that a variable is null.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert null $actual
*/
public static function assertNull($actual, string $message = ''): void
{
static::assertThat($actual, static::isNull(), $message);
}
/**
* Asserts that a variable is not null.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !null $actual
*/
public static function assertNotNull($actual, string $message = ''): void
{
static::assertThat($actual, static::logicalNot(static::isNull()), $message);
}
/**
* Asserts that a variable is finite.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertFinite($actual, string $message = ''): void
{
static::assertThat($actual, static::isFinite(), $message);
}
/**
* Asserts that a variable is infinite.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertInfinite($actual, string $message = ''): void
{
static::assertThat($actual, static::isInfinite(), $message);
}
/**
* Asserts that a variable is nan.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNan($actual, string $message = ''): void
{
static::assertThat($actual, static::isNan(), $message);
}
/**
* Asserts that a class has a specified attribute.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void
{
if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}
if (!\class_exists($className)) {
throw InvalidArgumentException::create(2, 'class name');
}
static::assertThat($className, new ClassHasAttribute($attributeName), $message);
}
/**
* Asserts that a class does not have a specified attribute.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void
{
if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}
if (!\class_exists($className)) {
throw InvalidArgumentException::create(2, 'class name');
}
static::assertThat(
$className,
new LogicalNot(
new ClassHasAttribute($attributeName)
),
$message
);
}
/**
* Asserts that a class has a specified static attribute.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
{
if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}
if (!\class_exists($className)) {
throw InvalidArgumentException::create(2, 'class name');
}
static::assertThat(
$className,
new ClassHasStaticAttribute($attributeName),
$message
);
}
/**
* Asserts that a class does not have a specified static attribute.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
{
if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}
if (!\class_exists($className)) {
throw InvalidArgumentException::create(2, 'class name');
}
static::assertThat(
$className,
new LogicalNot(
new ClassHasStaticAttribute($attributeName)
),
$message
);
}
/**
* Asserts that an object has a specified attribute.
*
* @param object $object
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void
{
if (!self::isValidObjectAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}
if (!\is_object($object)) {
throw InvalidArgumentException::create(2, 'object');
}
static::assertThat(
$object,
new ObjectHasAttribute($attributeName),
$message
);
}
/**
* Asserts that an object does not have a specified attribute.
*
* @param object $object
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void
{
if (!self::isValidObjectAttributeName($attributeName)) {
throw InvalidArgumentException::create(1, 'valid attribute name');
}
if (!\is_object($object)) {
throw InvalidArgumentException::create(2, 'object');
}
static::assertThat(
$object,
new LogicalNot(
new ObjectHasAttribute($attributeName)
),
$message
);
}
/**
* Asserts that two variables have the same type and value.
* Used on objects, it asserts that two variables reference
* the same object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-template ExpectedType
* @psalm-param ExpectedType $expected
* @psalm-assert =ExpectedType $actual
*/
public static function assertSame($expected, $actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsIdentical($expected),
$message
);
}
/**
* Asserts that a variable and an attribute of an object have the same type
* and value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeSame() is deprecated and will be removed in PHPUnit 9.');
static::assertSame(
$expected,
static::readAttribute($actualClassOrObject, $actualAttributeName),
$message
);
}
/**
* Asserts that two variables do not have the same type and value.
* Used on objects, it asserts that two variables do not reference
* the same object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotSame($expected, $actual, string $message = ''): void
{
if (\is_bool($expected) && \is_bool($actual)) {
static::assertNotEquals($expected, $actual, $message);
}
static::assertThat(
$actual,
new LogicalNot(
new IsIdentical($expected)
),
$message
);
}
/**
* Asserts that a variable and an attribute of an object do not have the
* same type and value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeNotSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
self::createWarning('assertAttributeNotSame() is deprecated and will be removed in PHPUnit 9.');
static::assertNotSame(
$expected,
static::readAttribute($actualClassOrObject, $actualAttributeName),
$message
);
}
/**
* Asserts that a variable is of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @psalm-template ExpectedType of object
* @psalm-param class-string<ExpectedType> $expected
* @psalm-assert ExpectedType $actual
*/
public static function assertInstanceOf(string $expected, $actual, string $message = ''): void
{
if (!\class_exists($expected) && !\interface_exists($expected)) {
throw InvalidArgumentException::create(1, 'class or interface name');
}
static::assertThat(
$actual,
new IsInstanceOf($expected),
$message
);
}
/**
* Asserts that an attribute is of a given type.
*
* @param object|string $classOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @psalm-param class-string $expected
*/
public static function assertAttributeInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void
{
self::createWarning('assertAttributeInstanceOf() is deprecated and will be removed in PHPUnit 9.');
static::assertInstanceOf(
$expected,
static::readAttribute($classOrObject, $attributeName),
$message
);
}
/**
* Asserts that a variable is not of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @psalm-template ExpectedType of object
* @psalm-param class-string<ExpectedType> $expected
* @psalm-assert !ExpectedType $actual
*/
public static function assertNotInstanceOf(string $expected, $actual, string $message = ''): void
{
if (!\class_exists($expected) && !\interface_exists($expected)) {
throw InvalidArgumentException::create(1, 'class or interface name');
}
static::assertThat(
$actual,
new LogicalNot(
new IsInstanceOf($expected)
),
$message
);
}
/**
* Asserts that an attribute is of a given type.
*
* @param object|string $classOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @psalm-param class-string $expected
*/
public static function assertAttributeNotInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void
{
self::createWarning('assertAttributeNotInstanceOf() is deprecated and will be removed in PHPUnit 9.');
static::assertNotInstanceOf(
$expected,
static::readAttribute($classOrObject, $attributeName),
$message
);
}
/**
* Asserts that a variable is of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369
* @codeCoverageIgnore
*/
public static function assertInternalType(string $expected, $actual, string $message = ''): void
{
self::createWarning('assertInternalType() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertIsArray(), assertIsBool(), assertIsFloat(), assertIsInt(), assertIsNumeric(), assertIsObject(), assertIsResource(), assertIsString(), assertIsScalar(), assertIsCallable(), or assertIsIterable() instead.');
static::assertThat(
$actual,
new IsType($expected),
$message
);
}
/**
* Asserts that an attribute is of a given type.
*
* @param object|string $classOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void
{
self::createWarning('assertAttributeInternalType() is deprecated and will be removed in PHPUnit 9.');
static::assertInternalType(
$expected,
static::readAttribute($classOrObject, $attributeName),
$message
);
}
/**
* Asserts that a variable is of type array.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert array $actual
*/
public static function assertIsArray($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_ARRAY),
$message
);
}
/**
* Asserts that a variable is of type bool.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert bool $actual
*/
public static function assertIsBool($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_BOOL),
$message
);
}
/**
* Asserts that a variable is of type float.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert float $actual
*/
public static function assertIsFloat($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_FLOAT),
$message
);
}
/**
* Asserts that a variable is of type int.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert int $actual
*/
public static function assertIsInt($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_INT),
$message
);
}
/**
* Asserts that a variable is of type numeric.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert numeric $actual
*/
public static function assertIsNumeric($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_NUMERIC),
$message
);
}
/**
* Asserts that a variable is of type object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert object $actual
*/
public static function assertIsObject($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_OBJECT),
$message
);
}
/**
* Asserts that a variable is of type resource.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert resource $actual
*/
public static function assertIsResource($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_RESOURCE),
$message
);
}
/**
* Asserts that a variable is of type string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert string $actual
*/
public static function assertIsString($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_STRING),
$message
);
}
/**
* Asserts that a variable is of type scalar.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert scalar $actual
*/
public static function assertIsScalar($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_SCALAR),
$message
);
}
/**
* Asserts that a variable is of type callable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert callable $actual
*/
public static function assertIsCallable($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_CALLABLE),
$message
);
}
/**
* Asserts that a variable is of type iterable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert iterable $actual
*/
public static function assertIsIterable($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_ITERABLE),
$message
);
}
/**
* Asserts that a variable is not of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369
* @codeCoverageIgnore
*/
public static function assertNotInternalType(string $expected, $actual, string $message = ''): void
{
self::createWarning('assertNotInternalType() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertIsNotArray(), assertIsNotBool(), assertIsNotFloat(), assertIsNotInt(), assertIsNotNumeric(), assertIsNotObject(), assertIsNotResource(), assertIsNotString(), assertIsNotScalar(), assertIsNotCallable(), or assertIsNotIterable() instead.');
static::assertThat(
$actual,
new LogicalNot(
new IsType($expected)
),
$message
);
}
/**
* Asserts that a variable is not of type array.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !array $actual
*/
public static function assertIsNotArray($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_ARRAY)),
$message
);
}
/**
* Asserts that a variable is not of type bool.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !bool $actual
*/
public static function assertIsNotBool($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_BOOL)),
$message
);
}
/**
* Asserts that a variable is not of type float.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !float $actual
*/
public static function assertIsNotFloat($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_FLOAT)),
$message
);
}
/**
* Asserts that a variable is not of type int.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !int $actual
*/
public static function assertIsNotInt($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_INT)),
$message
);
}
/**
* Asserts that a variable is not of type numeric.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !numeric $actual
*/
public static function assertIsNotNumeric($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_NUMERIC)),
$message
);
}
/**
* Asserts that a variable is not of type object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !object $actual
*/
public static function assertIsNotObject($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_OBJECT)),
$message
);
}
/**
* Asserts that a variable is not of type resource.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !resource $actual
*/
public static function assertIsNotResource($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_RESOURCE)),
$message
);
}
/**
* Asserts that a variable is not of type string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !string $actual
*/
public static function assertIsNotString($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_STRING)),
$message
);
}
/**
* Asserts that a variable is not of type scalar.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !scalar $actual
*/
public static function assertIsNotScalar($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_SCALAR)),
$message
);
}
/**
* Asserts that a variable is not of type callable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !callable $actual
*/
public static function assertIsNotCallable($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_CALLABLE)),
$message
);
}
/**
* Asserts that a variable is not of type iterable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !iterable $actual
*/
public static function assertIsNotIterable($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_ITERABLE)),
$message
);
}
/**
* Asserts that an attribute is of a given type.
*
* @param object|string $classOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function assertAttributeNotInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void
{
self::createWarning('assertAttributeNotInternalType() is deprecated and will be removed in PHPUnit 9.');
static::assertNotInternalType(
$expected,
static::readAttribute($classOrObject, $attributeName),
$message
);
}
/**
* Asserts that a string matches a given regular expression.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertRegExp(string $pattern, string $string, string $message = ''): void
{
static::assertThat($string, new RegularExpression($pattern), $message);
}
/**
* Asserts that a string does not match a given regular expression.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertNotRegExp(string $pattern, string $string, string $message = ''): void
{
static::assertThat(
$string,
new LogicalNot(
new RegularExpression($pattern)
),
$message
);
}
/**
* Assert that the size of two arrays (or `Countable` or `Traversable` objects)
* is the same.
*
* @param Countable|iterable $expected
* @param Countable|iterable $actual
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertSameSize($expected, $actual, string $message = ''): void
{
if (!$expected instanceof Countable && !\is_iterable($expected)) {
throw InvalidArgumentException::create(1, 'countable or iterable');
}
if (!$actual instanceof Countable && !\is_iterable($actual)) {
throw InvalidArgumentException::create(2, 'countable or iterable');
}
static::assertThat(
$actual,
new SameSize($expected),
$message
);
}
/**
* Assert that the size of two arrays (or `Countable` or `Traversable` objects)
* is not the same.
*
* @param Countable|iterable $expected
* @param Countable|iterable $actual
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertNotSameSize($expected, $actual, string $message = ''): void
{
if (!$expected instanceof Countable && !\is_iterable($expected)) {
throw InvalidArgumentException::create(1, 'countable or iterable');
}
if (!$actual instanceof Countable && !\is_iterable($actual)) {
throw InvalidArgumentException::create(2, 'countable or iterable');
}
static::assertThat(
$actual,
new LogicalNot(
new SameSize($expected)
),
$message
);
}
/**
* Asserts that a string matches a given format string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringMatchesFormat(string $format, string $string, string $message = ''): void
{
static::assertThat($string, new StringMatchesFormatDescription($format), $message);
}
/**
* Asserts that a string does not match a given format string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void
{
static::assertThat(
$string,
new LogicalNot(
new StringMatchesFormatDescription($format)
),
$message
);
}
/**
* Asserts that a string matches a given format file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
{
static::assertFileExists($formatFile, $message);
static::assertThat(
$string,
new StringMatchesFormatDescription(
\file_get_contents($formatFile)
),
$message
);
}
/**
* Asserts that a string does not match a given format string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
{
static::assertFileExists($formatFile, $message);
static::assertThat(
$string,
new LogicalNot(
new StringMatchesFormatDescription(
\file_get_contents($formatFile)
)
),
$message
);
}
/**
* Asserts that a string starts with a given prefix.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringStartsWith(string $prefix, string $string, string $message = ''): void
{
static::assertThat($string, new StringStartsWith($prefix), $message);
}
/**
* Asserts that a string starts not with a given prefix.
*
* @param string $prefix
* @param string $string
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringStartsNotWith($prefix, $string, string $message = ''): void
{
static::assertThat(
$string,
new LogicalNot(
new StringStartsWith($prefix)
),
$message
);
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringContainsString(string $needle, string $haystack, string $message = ''): void
{
$constraint = new StringContains($needle, false);
static::assertThat($haystack, $constraint, $message);
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
{
$constraint = new StringContains($needle, true);
static::assertThat($haystack, $constraint, $message);
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void
{
$constraint = new LogicalNot(new StringContains($needle));
static::assertThat($haystack, $constraint, $message);
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
{
$constraint = new LogicalNot(new StringContains($needle, true));
static::assertThat($haystack, $constraint, $message);
}
/**
* Asserts that a string ends with a given suffix.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringEndsWith(string $suffix, string $string, string $message = ''): void
{
static::assertThat($string, new StringEndsWith($suffix), $message);
}
/**
* Asserts that a string ends not with a given suffix.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void
{
static::assertThat(
$string,
new LogicalNot(
new StringEndsWith($suffix)
),
$message
);
}
/**
* Asserts that two XML files are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
{
$expected = Xml::loadFile($expectedFile);
$actual = Xml::loadFile($actualFile);
static::assertEquals($expected, $actual, $message);
}
/**
* Asserts that two XML files are not equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
{
$expected = Xml::loadFile($expectedFile);
$actual = Xml::loadFile($actualFile);
static::assertNotEquals($expected, $actual, $message);
}
/**
* Asserts that two XML documents are equal.
*
* @param DOMDocument|string $actualXml
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
{
$expected = Xml::loadFile($expectedFile);
$actual = Xml::load($actualXml);
static::assertEquals($expected, $actual, $message);
}
/**
* Asserts that two XML documents are not equal.
*
* @param DOMDocument|string $actualXml
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
{
$expected = Xml::loadFile($expectedFile);
$actual = Xml::load($actualXml);
static::assertNotEquals($expected, $actual, $message);
}
/**
* Asserts that two XML documents are equal.
*
* @param DOMDocument|string $expectedXml
* @param DOMDocument|string $actualXml
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
{
$expected = Xml::load($expectedXml);
$actual = Xml::load($actualXml);
static::assertEquals($expected, $actual, $message);
}
/**
* Asserts that two XML documents are not equal.
*
* @param DOMDocument|string $expectedXml
* @param DOMDocument|string $actualXml
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
{
$expected = Xml::load($expectedXml);
$actual = Xml::load($actualXml);
static::assertNotEquals($expected, $actual, $message);
}
/**
* Asserts that a hierarchy of DOMElements matches.
*
* @throws AssertionFailedError
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void
{
$expectedElement = Xml::import($expectedElement);
$actualElement = Xml::import($actualElement);
static::assertSame(
$expectedElement->tagName,
$actualElement->tagName,
$message
);
if ($checkAttributes) {
static::assertSame(
$expectedElement->attributes->length,
$actualElement->attributes->length,
\sprintf(
'%s%sNumber of attributes on node "%s" does not match',
$message,
!empty($message) ? "\n" : '',
$expectedElement->tagName
)
);
for ($i = 0; $i < $expectedElement->attributes->length; $i++) {
$expectedAttribute = $expectedElement->attributes->item($i);
$actualAttribute = $actualElement->attributes->getNamedItem($expectedAttribute->name);
\assert($expectedAttribute instanceof \DOMAttr);
if (!$actualAttribute) {
static::fail(
\sprintf(
'%s%sCould not find attribute "%s" on node "%s"',
$message,
!empty($message) ? "\n" : '',
$expectedAttribute->name,
$expectedElement->tagName
)
);
}
}
}
Xml::removeCharacterDataNodes($expectedElement);
Xml::removeCharacterDataNodes($actualElement);
static::assertSame(
$expectedElement->childNodes->length,
$actualElement->childNodes->length,
\sprintf(
'%s%sNumber of child nodes of "%s" differs',
$message,
!empty($message) ? "\n" : '',
$expectedElement->tagName
)
);
for ($i = 0; $i < $expectedElement->childNodes->length; $i++) {
static::assertEqualXMLStructure(
$expectedElement->childNodes->item($i),
$actualElement->childNodes->item($i),
$checkAttributes,
$message
);
}
}
/**
* Evaluates a PHPUnit\Framework\Constraint matcher object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertThat($value, Constraint $constraint, string $message = ''): void
{
self::$count += \count($constraint);
$constraint->evaluate($value, $message);
}
/**
* Asserts that a string is a valid JSON string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertJson(string $actualJson, string $message = ''): void
{
static::assertThat($actualJson, static::isJson(), $message);
}
/**
* Asserts that two given JSON encoded objects or arrays are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void
{
static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);
static::assertThat($actualJson, new JsonMatches($expectedJson), $message);
}
/**
* Asserts that two given JSON encoded objects or arrays are not equal.
*
* @param string $expectedJson
* @param string $actualJson
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void
{
static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);
static::assertThat(
$actualJson,
new LogicalNot(
new JsonMatches($expectedJson)
),
$message
);
}
/**
* Asserts that the generated JSON encoded object and the content of the given file are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);
$expectedJson = \file_get_contents($expectedFile);
static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);
static::assertThat($actualJson, new JsonMatches($expectedJson), $message);
}
/**
* Asserts that the generated JSON encoded object and the content of the given file are not equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);
$expectedJson = \file_get_contents($expectedFile);
static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);
static::assertThat(
$actualJson,
new LogicalNot(
new JsonMatches($expectedJson)
),
$message
);
}
/**
* Asserts that two JSON files are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);
static::assertFileExists($actualFile, $message);
$actualJson = \file_get_contents($actualFile);
$expectedJson = \file_get_contents($expectedFile);
static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);
$constraintExpected = new JsonMatches(
$expectedJson
);
$constraintActual = new JsonMatches($actualJson);
static::assertThat($expectedJson, $constraintActual, $message);
static::assertThat($actualJson, $constraintExpected, $message);
}
/**
* Asserts that two JSON files are not equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
{
static::assertFileExists($expectedFile, $message);
static::assertFileExists($actualFile, $message);
$actualJson = \file_get_contents($actualFile);
$expectedJson = \file_get_contents($expectedFile);
static::assertJson($expectedJson, $message);
static::assertJson($actualJson, $message);
$constraintExpected = new JsonMatches(
$expectedJson
);
$constraintActual = new JsonMatches($actualJson);
static::assertThat($expectedJson, new LogicalNot($constraintActual), $message);
static::assertThat($actualJson, new LogicalNot($constraintExpected), $message);
}
/**
* @throws Exception
*/
public static function logicalAnd(): LogicalAnd
{
$constraints = \func_get_args();
$constraint = new LogicalAnd;
$constraint->setConstraints($constraints);
return $constraint;
}
public static function logicalOr(): LogicalOr
{
$constraints = \func_get_args();
$constraint = new LogicalOr;
$constraint->setConstraints($constraints);
return $constraint;
}
public static function logicalNot(Constraint $constraint): LogicalNot
{
return new LogicalNot($constraint);
}
public static function logicalXor(): LogicalXor
{
$constraints = \func_get_args();
$constraint = new LogicalXor;
$constraint->setConstraints($constraints);
return $constraint;
}
public static function anything(): IsAnything
{
return new IsAnything;
}
public static function isTrue(): IsTrue
{
return new IsTrue;
}
public static function callback(callable $callback): Callback
{
return new Callback($callback);
}
public static function isFalse(): IsFalse
{
return new IsFalse;
}
public static function isJson(): IsJson
{
return new IsJson;
}
public static function isNull(): IsNull
{
return new IsNull;
}
public static function isFinite(): IsFinite
{
return new IsFinite;
}
public static function isInfinite(): IsInfinite
{
return new IsInfinite;
}
public static function isNan(): IsNan
{
return new IsNan;
}
/**
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function attribute(Constraint $constraint, string $attributeName): Attribute
{
self::createWarning('attribute() is deprecated and will be removed in PHPUnit 9.');
return new Attribute($constraint, $attributeName);
}
public static function contains($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): TraversableContains
{
return new TraversableContains($value, $checkForObjectIdentity, $checkForNonObjectIdentity);
}
public static function containsOnly(string $type): TraversableContainsOnly
{
return new TraversableContainsOnly($type);
}
public static function containsOnlyInstancesOf(string $className): TraversableContainsOnly
{
return new TraversableContainsOnly($className, false);
}
/**
* @param int|string $key
*/
public static function arrayHasKey($key): ArrayHasKey
{
return new ArrayHasKey($key);
}
public static function equalTo($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): IsEqual
{
return new IsEqual($value, $delta, $maxDepth, $canonicalize, $ignoreCase);
}
/**
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function attributeEqualTo(string $attributeName, $value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): Attribute
{
self::createWarning('attributeEqualTo() is deprecated and will be removed in PHPUnit 9.');
return static::attribute(
static::equalTo(
$value,
$delta,
$maxDepth,
$canonicalize,
$ignoreCase
),
$attributeName
);
}
public static function isEmpty(): IsEmpty
{
return new IsEmpty;
}
public static function isWritable(): IsWritable
{
return new IsWritable;
}
public static function isReadable(): IsReadable
{
return new IsReadable;
}
public static function directoryExists(): DirectoryExists
{
return new DirectoryExists;
}
public static function fileExists(): FileExists
{
return new FileExists;
}
public static function greaterThan($value): GreaterThan
{
return new GreaterThan($value);
}
public static function greaterThanOrEqual($value): LogicalOr
{
return static::logicalOr(
new IsEqual($value),
new GreaterThan($value)
);
}
public static function classHasAttribute(string $attributeName): ClassHasAttribute
{
return new ClassHasAttribute($attributeName);
}
public static function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute
{
return new ClassHasStaticAttribute($attributeName);
}
public static function objectHasAttribute($attributeName): ObjectHasAttribute
{
return new ObjectHasAttribute($attributeName);
}
public static function identicalTo($value): IsIdentical
{
return new IsIdentical($value);
}
public static function isInstanceOf(string $className): IsInstanceOf
{
return new IsInstanceOf($className);
}
public static function isType(string $type): IsType
{
return new IsType($type);
}
public static function lessThan($value): LessThan
{
return new LessThan($value);
}
public static function lessThanOrEqual($value): LogicalOr
{
return static::logicalOr(
new IsEqual($value),
new LessThan($value)
);
}
public static function matchesRegularExpression(string $pattern): RegularExpression
{
return new RegularExpression($pattern);
}
public static function matches(string $string): StringMatchesFormatDescription
{
return new StringMatchesFormatDescription($string);
}
public static function stringStartsWith($prefix): StringStartsWith
{
return new StringStartsWith($prefix);
}
public static function stringContains(string $string, bool $case = true): StringContains
{
return new StringContains($string, $case);
}
public static function stringEndsWith(string $suffix): StringEndsWith
{
return new StringEndsWith($suffix);
}
public static function countOf(int $count): Count
{
return new Count($count);
}
/**
* Fails a test with the given message.
*
* @throws AssertionFailedError
*
* @psalm-return never-return
*/
public static function fail(string $message = ''): void
{
self::$count++;
throw new AssertionFailedError($message);
}
/**
* Returns the value of an attribute of a class or an object.
* This also works for attributes that are declared protected or private.
*
* @param object|string $classOrObject
*
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function readAttribute($classOrObject, string $attributeName)
{
self::createWarning('readAttribute() is deprecated and will be removed in PHPUnit 9.');
if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(2, 'valid attribute name');
}
if (\is_string($classOrObject)) {
if (!\class_exists($classOrObject)) {
throw InvalidArgumentException::create(
1,
'class name'
);
}
return static::getStaticAttribute(
$classOrObject,
$attributeName
);
}
if (\is_object($classOrObject)) {
return static::getObjectAttribute(
$classOrObject,
$attributeName
);
}
throw InvalidArgumentException::create(
1,
'class name or object'
);
}
/**
* Returns the value of a static attribute.
* This also works for attributes that are declared protected or private.
*
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function getStaticAttribute(string $className, string $attributeName)
{
self::createWarning('getStaticAttribute() is deprecated and will be removed in PHPUnit 9.');
if (!\class_exists($className)) {
throw InvalidArgumentException::create(1, 'class name');
}
if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(2, 'valid attribute name');
}
try {
$class = new ReflectionClass($className);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
while ($class) {
$attributes = $class->getStaticProperties();
if (\array_key_exists($attributeName, $attributes)) {
return $attributes[$attributeName];
}
$class = $class->getParentClass();
}
throw new Exception(
\sprintf(
'Attribute "%s" not found in class.',
$attributeName
)
);
}
/**
* Returns the value of an object's attribute.
* This also works for attributes that are declared protected or private.
*
* @param object $object
*
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
public static function getObjectAttribute($object, string $attributeName)
{
self::createWarning('getObjectAttribute() is deprecated and will be removed in PHPUnit 9.');
if (!\is_object($object)) {
throw InvalidArgumentException::create(1, 'object');
}
if (!self::isValidClassAttributeName($attributeName)) {
throw InvalidArgumentException::create(2, 'valid attribute name');
}
$reflector = new ReflectionObject($object);
do {
try {
$attribute = $reflector->getProperty($attributeName);
if (!$attribute || $attribute->isPublic()) {
return $object->$attributeName;
}
$attribute->setAccessible(true);
$value = $attribute->getValue($object);
$attribute->setAccessible(false);
return $value;
} catch (\ReflectionException $e) {
}
} while ($reflector = $reflector->getParentClass());
throw new Exception(
\sprintf(
'Attribute "%s" not found in object.',
$attributeName
)
);
}
/**
* Mark the test as incomplete.
*
* @throws IncompleteTestError
*/
public static function markTestIncomplete(string $message = ''): void
{
throw new IncompleteTestError($message);
}
/**
* Mark the test as skipped.
*
* @throws SkippedTestError
* @throws SyntheticSkippedError
*/
public static function markTestSkipped(string $message = ''): void
{
if ($hint = self::detectLocationHint($message)) {
$trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
\array_unshift($trace, $hint);
throw new SyntheticSkippedError($hint['message'], 0, $hint['file'], (int) $hint['line'], $trace);
}
throw new SkippedTestError($message);
}
/**
* Return the current assertion count.
*/
public static function getCount(): int
{
return self::$count;
}
/**
* Reset the assertion counter.
*/
public static function resetCount(): void
{
self::$count = 0;
}
private static function detectLocationHint(string $message): ?array
{
$hint = null;
$lines = \preg_split('/\r\n|\r|\n/', $message);
while (\strpos($lines[0], '__OFFSET') !== false) {
$offset = \explode('=', \array_shift($lines));
if ($offset[0] === '__OFFSET_FILE') {
$hint['file'] = $offset[1];
}
if ($offset[0] === '__OFFSET_LINE') {
$hint['line'] = $offset[1];
}
}
if ($hint) {
$hint['message'] = \implode(\PHP_EOL, $lines);
}
return $hint;
}
private static function isValidObjectAttributeName(string $attributeName): bool
{
return (bool) \preg_match('/[^\x00-\x1f\x7f-\x9f]+/', $attributeName);
}
private static function isValidClassAttributeName(string $attributeName): bool
{
return (bool) \preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName);
}
/**
* @codeCoverageIgnore
*/
private static function createWarning(string $warning): void
{
foreach (\debug_backtrace() as $step) {
if (isset($step['object']) && $step['object'] instanceof TestCase) {
\assert($step['object'] instanceof TestCase);
$step['object']->addWarning($warning);
break;
}
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class IncompleteTestCase extends TestCase
{
/**
* @var bool
*/
protected $backupGlobals = false;
/**
* @var bool
*/
protected $backupStaticAttributes = false;
/**
* @var bool
*/
protected $runTestInSeparateProcess = false;
/**
* @var bool
*/
protected $useErrorHandler = false;
/**
* @var string
*/
private $message;
public function __construct(string $className, string $methodName, string $message = '')
{
parent::__construct($className . '::' . $methodName);
$this->message = $message;
}
public function getMessage(): string
{
return $this->message;
}
/**
* Returns a string representation of the test case.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
return $this->getName();
}
/**
* @throws Exception
*/
protected function runTest(): void
{
$this->markTestIncomplete($this->message);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use PHPUnit\Util\Test as TestUtil;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class DataProviderTestSuite extends TestSuite
{
/**
* @var string[]
*/
private $dependencies = [];
/**
* @param string[] $dependencies
*/
public function setDependencies(array $dependencies): void
{
$this->dependencies = $dependencies;
foreach ($this->tests as $test) {
if (!$test instanceof TestCase) {
continue;
}
$test->setDependencies($dependencies);
}
}
public function getDependencies(): array
{
return $this->dependencies;
}
public function hasDependencies(): bool
{
return \count($this->dependencies) > 0;
}
/**
* Returns the size of the each test created using the data provider(s)
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function getSize(): int
{
[$className, $methodName] = \explode('::', $this->getName());
return TestUtil::getSize($className, $methodName);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @deprecated Use the `TestHook` interfaces instead
*/
interface TestListener
{
/**
* An error occurred.
*
* @deprecated Use `AfterTestErrorHook::executeAfterTestError` instead
*/
public function addError(Test $test, \Throwable $t, float $time): void;
/**
* A warning occurred.
*
* @deprecated Use `AfterTestWarningHook::executeAfterTestWarning` instead
*/
public function addWarning(Test $test, Warning $e, float $time): void;
/**
* A failure occurred.
*
* @deprecated Use `AfterTestFailureHook::executeAfterTestFailure` instead
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void;
/**
* Incomplete test.
*
* @deprecated Use `AfterIncompleteTestHook::executeAfterIncompleteTest` instead
*/
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void;
/**
* Risky test.
*
* @deprecated Use `AfterRiskyTestHook::executeAfterRiskyTest` instead
*/
public function addRiskyTest(Test $test, \Throwable $t, float $time): void;
/**
* Skipped test.
*
* @deprecated Use `AfterSkippedTestHook::executeAfterSkippedTest` instead
*/
public function addSkippedTest(Test $test, \Throwable $t, float $time): void;
/**
* A test suite started.
*/
public function startTestSuite(TestSuite $suite): void;
/**
* A test suite ended.
*/
public function endTestSuite(TestSuite $suite): void;
/**
* A test started.
*
* @deprecated Use `BeforeTestHook::executeBeforeTest` instead
*/
public function startTest(Test $test): void;
/**
* A test ended.
*
* @deprecated Use `AfterTestHook::executeAfterTest` instead
*/
public function endTest(Test $test, float $time): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class SkippedTestSuiteError extends AssertionFailedError implements SkippedTest
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class AssertionFailedError extends Exception implements SelfDescribing
{
/**
* Wrapper for getMessage() which is declared as final.
*/
public function toString(): string
{
return $this->getMessage();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class RiskyTestError extends AssertionFailedError
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class SyntheticError extends AssertionFailedError
{
/**
* The synthetic file.
*
* @var string
*/
protected $syntheticFile = '';
/**
* The synthetic line number.
*
* @var int
*/
protected $syntheticLine = 0;
/**
* The synthetic trace.
*
* @var array
*/
protected $syntheticTrace = [];
public function __construct(string $message, int $code, string $file, int $line, array $trace)
{
parent::__construct($message, $code);
$this->syntheticFile = $file;
$this->syntheticLine = $line;
$this->syntheticTrace = $trace;
}
public function getSyntheticFile(): string
{
return $this->syntheticFile;
}
public function getSyntheticLine(): int
{
return $this->syntheticLine;
}
public function getSyntheticTrace(): array
{
return $this->syntheticTrace;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class CodeCoverageException extends Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class OutputError extends AssertionFailedError
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use PHPUnit\Util\Filter;
/**
* Base class for all PHPUnit Framework exceptions.
*
* Ensures that exceptions thrown during a test run do not leave stray
* references behind.
*
* Every Exception contains a stack trace. Each stack frame contains the 'args'
* of the called function. The function arguments can contain references to
* instantiated objects. The references prevent the objects from being
* destructed (until test results are eventually printed), so memory cannot be
* freed up.
*
* With enabled process isolation, test results are serialized in the child
* process and unserialized in the parent process. The stack trace of Exceptions
* may contain objects that cannot be serialized or unserialized (e.g., PDO
* connections). Unserializing user-space objects from the child process into
* the parent would break the intended encapsulation of process isolation.
*
* @see http://fabien.potencier.org/article/9/php-serialization-stack-traces-and-exceptions
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class Exception extends \RuntimeException implements \PHPUnit\Exception
{
/**
* @var array
*/
protected $serializableTrace;
public function __construct($message = '', $code = 0, \Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->serializableTrace = $this->getTrace();
foreach ($this->serializableTrace as $i => $call) {
unset($this->serializableTrace[$i]['args']);
}
}
public function __toString(): string
{
$string = TestFailure::exceptionToString($this);
if ($trace = Filter::getFilteredStacktrace($this)) {
$string .= "\n" . $trace;
}
return $string;
}
public function __sleep(): array
{
return \array_keys(\get_object_vars($this));
}
/**
* Returns the serializable trace (without 'args').
*/
public function getSerializableTrace(): array
{
return $this->serializableTrace;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class SkippedTestError extends AssertionFailedError implements SkippedTest
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class PHPTAssertionFailedError extends SyntheticError
{
/**
* @var string
*/
private $diff;
public function __construct(string $message, int $code, string $file, int $line, array $trace, string $diff)
{
parent::__construct($message, $code, $file, $line, $trace);
$this->diff = $diff;
}
public function getDiff(): string
{
return $this->diff;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvalidArgumentException extends Exception
{
public static function create(int $argument, string $type): self
{
$stack = \debug_backtrace();
return new self(
\sprintf(
'Argument #%d of %s::%s() must be %s %s',
$argument,
$stack[1]['class'],
$stack[1]['function'],
\in_array(\lcfirst($type)[0], ['a', 'e', 'i', 'o', 'u']) ? 'an' : 'a',
$type
)
);
}
private function __construct(string $message = '', int $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
final class UnexpectedValueException extends Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class UnintentionallyCoveredCodeError extends RiskyTestError
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class IncompleteTestError extends AssertionFailedError implements IncompleteTest
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MissingCoversAnnotationException extends RiskyTestError
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class SyntheticSkippedError extends SyntheticError implements SkippedTest
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvalidCoversTargetException extends CodeCoverageException
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class CoveredCodeNotExecutedException extends RiskyTestError
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use SebastianBergmann\Comparator\ComparisonFailure;
/**
* Exception for expectations which failed their check.
*
* The exception contains the error message and optionally a
* SebastianBergmann\Comparator\ComparisonFailure which is used to
* generate diff output of the failed expectations.
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ExpectationFailedException extends AssertionFailedError
{
/**
* @var ComparisonFailure
*/
protected $comparisonFailure;
public function __construct(string $message, ComparisonFailure $comparisonFailure = null, \Exception $previous = null)
{
$this->comparisonFailure = $comparisonFailure;
parent::__construct($message, 0, $previous);
}
public function getComparisonFailure(): ?ComparisonFailure
{
return $this->comparisonFailure;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Warning extends Exception implements SelfDescribing
{
/**
* Wrapper for getMessage() which is declared as final.
*/
public function toString(): string
{
return $this->getMessage();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvalidDataProviderException extends Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException;
use Doctrine\Instantiator\Instantiator;
use PHPUnit\Framework\InvalidArgumentException;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Generator
{
/**
* @var array
*/
private const BLACKLISTED_METHOD_NAMES = [
'__CLASS__' => true,
'__DIR__' => true,
'__FILE__' => true,
'__FUNCTION__' => true,
'__LINE__' => true,
'__METHOD__' => true,
'__NAMESPACE__' => true,
'__TRAIT__' => true,
'__clone' => true,
'__halt_compiler' => true,
];
/**
* @var array
*/
private static $cache = [];
/**
* @var \Text_Template[]
*/
private static $templates = [];
/**
* Returns a mock object for the specified class.
*
* @param string|string[] $type
* @param null|array $methods
*
* @throws RuntimeException
*/
public function getMock($type, $methods = [], array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false, object $proxyTarget = null, bool $allowMockingUnknownTypes = true, bool $returnValueGeneration = true): MockObject
{
if (!\is_array($type) && !\is_string($type)) {
throw InvalidArgumentException::create(1, 'array or string');
}
if (!\is_array($methods) && null !== $methods) {
throw InvalidArgumentException::create(2, 'array');
}
if ($type === 'Traversable' || $type === '\\Traversable') {
$type = 'Iterator';
}
if (\is_array($type)) {
$type = \array_unique(
\array_map(
static function ($type) {
if ($type === 'Traversable' ||
$type === '\\Traversable' ||
$type === '\\Iterator') {
return 'Iterator';
}
return $type;
},
$type
)
);
}
if (!$allowMockingUnknownTypes) {
if (\is_array($type)) {
foreach ($type as $_type) {
if (!\class_exists($_type, $callAutoload) &&
!\interface_exists($_type, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Cannot stub or mock class or interface "%s" which does not exist',
$_type
)
);
}
}
} elseif (!\class_exists($type, $callAutoload) && !\interface_exists($type, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Cannot stub or mock class or interface "%s" which does not exist',
$type
)
);
}
}
if (null !== $methods) {
foreach ($methods as $method) {
if (!\preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', (string) $method)) {
throw new RuntimeException(
\sprintf(
'Cannot stub or mock method with invalid name "%s"',
$method
)
);
}
}
if ($methods !== \array_unique($methods)) {
throw new RuntimeException(
\sprintf(
'Cannot stub or mock using a method list that contains duplicates: "%s" (duplicate: "%s")',
\implode(', ', $methods),
\implode(', ', \array_unique(\array_diff_assoc($methods, \array_unique($methods))))
)
);
}
}
if ($mockClassName !== '' && \class_exists($mockClassName, false)) {
try {
$reflector = new \ReflectionClass($mockClassName);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$reflector->implementsInterface(MockObject::class)) {
throw new RuntimeException(
\sprintf(
'Class "%s" already exists.',
$mockClassName
)
);
}
}
if (!$callOriginalConstructor && $callOriginalMethods) {
throw new RuntimeException(
'Proxying to original methods requires invoking the original constructor'
);
}
$mock = $this->generate(
$type,
$methods,
$mockClassName,
$callOriginalClone,
$callAutoload,
$cloneArguments,
$callOriginalMethods
);
return $this->getObject(
$mock,
$type,
$callOriginalConstructor,
$callAutoload,
$arguments,
$callOriginalMethods,
$proxyTarget,
$returnValueGeneration
);
}
/**
* Returns a mock object for the specified abstract class with all abstract
* methods of the class mocked. Concrete methods to mock can be specified with
* the $mockedMethods parameter
*
* @throws RuntimeException
*/
public function getMockForAbstractClass(string $originalClassName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = true): MockObject
{
if (\class_exists($originalClassName, $callAutoload) ||
\interface_exists($originalClassName, $callAutoload)) {
try {
$reflector = new \ReflectionClass($originalClassName);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$methods = $mockedMethods;
foreach ($reflector->getMethods() as $method) {
if ($method->isAbstract() && !\in_array($method->getName(), $methods, true)) {
$methods[] = $method->getName();
}
}
if (empty($methods)) {
$methods = null;
}
return $this->getMock(
$originalClassName,
$methods,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$cloneArguments
);
}
throw new RuntimeException(
\sprintf('Class "%s" does not exist.', $originalClassName)
);
}
/**
* Returns a mock object for the specified trait with all abstract methods
* of the trait mocked. Concrete methods to mock can be specified with the
* `$mockedMethods` parameter.
*
* @throws RuntimeException
*/
public function getMockForTrait(string $traitName, array $arguments = [], string $mockClassName = '', bool $callOriginalConstructor = true, bool $callOriginalClone = true, bool $callAutoload = true, array $mockedMethods = [], bool $cloneArguments = true): MockObject
{
if (!\trait_exists($traitName, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Trait "%s" does not exist.',
$traitName
)
);
}
$className = $this->generateClassName(
$traitName,
'',
'Trait_'
);
$classTemplate = $this->getTemplate('trait_class.tpl');
$classTemplate->setVar(
[
'prologue' => 'abstract ',
'class_name' => $className['className'],
'trait_name' => $traitName,
]
);
$mockTrait = new MockTrait($classTemplate->render(), $className['className']);
$mockTrait->generate();
return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments);
}
/**
* Returns an object for the specified trait.
*
* @throws RuntimeException
*/
public function getObjectForTrait(string $traitName, string $traitClassName = '', bool $callAutoload = true, bool $callOriginalConstructor = false, array $arguments = []): object
{
if (!\trait_exists($traitName, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Trait "%s" does not exist.',
$traitName
)
);
}
$className = $this->generateClassName(
$traitName,
$traitClassName,
'Trait_'
);
$classTemplate = $this->getTemplate('trait_class.tpl');
$classTemplate->setVar(
[
'prologue' => '',
'class_name' => $className['className'],
'trait_name' => $traitName,
]
);
return $this->getObject(
new MockTrait(
$classTemplate->render(),
$className['className']
),
'',
$callOriginalConstructor,
$callAutoload,
$arguments
);
}
public function generate($type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false): MockClass
{
if (\is_array($type)) {
\sort($type);
}
if ($mockClassName !== '') {
return $this->generateMock(
$type,
$methods,
$mockClassName,
$callOriginalClone,
$callAutoload,
$cloneArguments,
$callOriginalMethods
);
}
$key = \md5(
\is_array($type) ? \implode('_', $type) : $type .
\serialize($methods) .
\serialize($callOriginalClone) .
\serialize($cloneArguments) .
\serialize($callOriginalMethods)
);
if (!isset(self::$cache[$key])) {
self::$cache[$key] = $this->generateMock(
$type,
$methods,
$mockClassName,
$callOriginalClone,
$callAutoload,
$cloneArguments,
$callOriginalMethods
);
}
return self::$cache[$key];
}
/**
* @throws RuntimeException
*/
public function generateClassFromWsdl(string $wsdlFile, string $className, array $methods = [], array $options = []): string
{
if (!\extension_loaded('soap')) {
throw new RuntimeException(
'The SOAP extension is required to generate a mock object from WSDL.'
);
}
$options = \array_merge($options, ['cache_wsdl' => \WSDL_CACHE_NONE]);
try {
$client = new \SoapClient($wsdlFile, $options);
$_methods = \array_unique($client->__getFunctions());
unset($client);
} catch (\SoapFault $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
\sort($_methods);
$methodTemplate = $this->getTemplate('wsdl_method.tpl');
$methodsBuffer = '';
foreach ($_methods as $method) {
\preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\(/', $method, $matches, \PREG_OFFSET_CAPTURE);
$lastFunction = \array_pop($matches[0]);
$nameStart = $lastFunction[1];
$nameEnd = $nameStart + \strlen($lastFunction[0]) - 1;
$name = \str_replace('(', '', $lastFunction[0]);
if (empty($methods) || \in_array($name, $methods, true)) {
$args = \explode(
',',
\str_replace(')', '', \substr($method, $nameEnd + 1))
);
foreach (\range(0, \count($args) - 1) as $i) {
$args[$i] = \substr($args[$i], \strpos($args[$i], '$'));
}
$methodTemplate->setVar(
[
'method_name' => $name,
'arguments' => \implode(', ', $args),
]
);
$methodsBuffer .= $methodTemplate->render();
}
}
$optionsBuffer = '[';
foreach ($options as $key => $value) {
$optionsBuffer .= $key . ' => ' . $value;
}
$optionsBuffer .= ']';
$classTemplate = $this->getTemplate('wsdl_class.tpl');
$namespace = '';
if (\strpos($className, '\\') !== false) {
$parts = \explode('\\', $className);
$className = \array_pop($parts);
$namespace = 'namespace ' . \implode('\\', $parts) . ';' . "\n\n";
}
$classTemplate->setVar(
[
'namespace' => $namespace,
'class_name' => $className,
'wsdl' => $wsdlFile,
'options' => $optionsBuffer,
'methods' => $methodsBuffer,
]
);
return $classTemplate->render();
}
/**
* @throws RuntimeException
*
* @return string[]
*/
public function getClassMethods(string $className): array
{
try {
$class = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$methods = [];
foreach ($class->getMethods() as $method) {
if ($method->isPublic() || $method->isAbstract()) {
$methods[] = $method->getName();
}
}
return $methods;
}
/**
* @throws RuntimeException
*
* @return MockMethod[]
*/
public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments): array
{
try {
$class = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$methods = [];
foreach ($class->getMethods() as $method) {
if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) {
$methods[] = MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments);
}
}
return $methods;
}
/**
* @return \ReflectionMethod[]
*/
private function getInterfaceOwnMethods(string $interfaceName): array
{
try {
$reflect = new \ReflectionClass($interfaceName);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$methods = [];
foreach ($reflect->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() === $interfaceName) {
$methods[] = $method;
}
}
return $methods;
}
private function getObject(MockType $mockClass, $type = '', bool $callOriginalConstructor = false, bool $callAutoload = false, array $arguments = [], bool $callOriginalMethods = false, object $proxyTarget = null, bool $returnValueGeneration = true)
{
$className = $mockClass->generate();
if ($callOriginalConstructor) {
if (\count($arguments) === 0) {
$object = new $className;
} else {
try {
$class = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$object = $class->newInstanceArgs($arguments);
}
} else {
try {
$object = (new Instantiator)->instantiate($className);
} catch (InstantiatorException $exception) {
throw new RuntimeException($exception->getMessage());
}
}
if ($callOriginalMethods) {
if (!\is_object($proxyTarget)) {
if (\count($arguments) === 0) {
$proxyTarget = new $type;
} else {
try {
$class = new \ReflectionClass($type);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$proxyTarget = $class->newInstanceArgs($arguments);
}
}
$object->__phpunit_setOriginalObject($proxyTarget);
}
if ($object instanceof MockObject) {
$object->__phpunit_setReturnValueGeneration($returnValueGeneration);
}
return $object;
}
/**
* @param array|string $type
*
* @throws RuntimeException
*/
private function generateMock($type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods): MockClass
{
$classTemplate = $this->getTemplate('mocked_class.tpl');
$additionalInterfaces = [];
$mockedCloneMethod = false;
$unmockedCloneMethod = false;
$isClass = false;
$isInterface = false;
$class = null;
$mockMethods = new MockMethodSet;
if (\is_array($type)) {
$interfaceMethods = [];
foreach ($type as $_type) {
if (!\interface_exists($_type, $callAutoload)) {
throw new RuntimeException(
\sprintf(
'Interface "%s" does not exist.',
$_type
)
);
}
$additionalInterfaces[] = $_type;
try {
$typeClass = new \ReflectionClass($_type);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
foreach ($this->getClassMethods($_type) as $methodTrait) {
if (\in_array($methodTrait, $interfaceMethods, true)) {
throw new RuntimeException(
\sprintf(
'Duplicate method "%s" not allowed.',
$methodTrait
)
);
}
try {
$methodReflection = $typeClass->getMethod($methodTrait);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($this->canMockMethod($methodReflection)) {
$mockMethods->addMethods(
MockMethod::fromReflection($methodReflection, $callOriginalMethods, $cloneArguments)
);
$interfaceMethods[] = $methodTrait;
}
}
}
unset($interfaceMethods);
}
$mockClassName = $this->generateClassName(
$type,
$mockClassName,
'Mock_'
);
if (\class_exists($mockClassName['fullClassName'], $callAutoload)) {
$isClass = true;
} elseif (\interface_exists($mockClassName['fullClassName'], $callAutoload)) {
$isInterface = true;
}
if (!$isClass && !$isInterface) {
$prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";
if (!empty($mockClassName['namespaceName'])) {
$prologue = 'namespace ' . $mockClassName['namespaceName'] .
" {\n\n" . $prologue . "}\n\n" .
"namespace {\n\n";
$epilogue = "\n\n}";
}
$mockedCloneMethod = true;
} else {
try {
$class = new \ReflectionClass($mockClassName['fullClassName']);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($class->isFinal()) {
throw new RuntimeException(
\sprintf(
'Class "%s" is declared "final" and cannot be mocked.',
$mockClassName['fullClassName']
)
);
}
// @see https://github.com/sebastianbergmann/phpunit/issues/2995
if ($isInterface && $class->implementsInterface(\Throwable::class)) {
$actualClassName = \Exception::class;
$additionalInterfaces[] = $class->getName();
$isInterface = false;
try {
$class = new \ReflectionClass($actualClassName);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
foreach ($this->getInterfaceOwnMethods($mockClassName['fullClassName']) as $methodTrait) {
$methodName = $methodTrait->getName();
if ($class->hasMethod($methodName)) {
try {
$classMethod = $class->getMethod($methodName);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$this->canMockMethod($classMethod)) {
continue;
}
}
$mockMethods->addMethods(
MockMethod::fromReflection($methodTrait, $callOriginalMethods, $cloneArguments)
);
}
$mockClassName = $this->generateClassName(
$actualClassName,
'',
'Mock_'
);
}
// @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103
if ($isInterface && $class->implementsInterface(\Traversable::class) &&
!$class->implementsInterface(\Iterator::class) &&
!$class->implementsInterface(\IteratorAggregate::class)) {
$additionalInterfaces[] = \Iterator::class;
$mockMethods->addMethods(
...$this->mockClassMethods(\Iterator::class, $callOriginalMethods, $cloneArguments)
);
}
if ($class->hasMethod('__clone')) {
try {
$cloneMethod = $class->getMethod('__clone');
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$cloneMethod->isFinal()) {
if ($callOriginalClone && !$isInterface) {
$unmockedCloneMethod = true;
} else {
$mockedCloneMethod = true;
}
}
} else {
$mockedCloneMethod = true;
}
}
if ($explicitMethods === [] &&
($isClass || $isInterface)) {
$mockMethods->addMethods(
...$this->mockClassMethods($mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments)
);
}
if (\is_array($explicitMethods)) {
foreach ($explicitMethods as $methodName) {
if ($class !== null && $class->hasMethod($methodName)) {
try {
$methodTrait = $class->getMethod($methodName);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($this->canMockMethod($methodTrait)) {
$mockMethods->addMethods(
MockMethod::fromReflection($methodTrait, $callOriginalMethods, $cloneArguments)
);
}
} else {
$mockMethods->addMethods(
MockMethod::fromName(
$mockClassName['fullClassName'],
$methodName,
$cloneArguments
)
);
}
}
}
$mockedMethods = '';
$configurable = [];
foreach ($mockMethods->asArray() as $mockMethod) {
$mockedMethods .= $mockMethod->generateCode();
$configurable[] = new ConfigurableMethod($mockMethod->getName(), $mockMethod->getReturnType());
}
$methodTrait = '';
if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) {
$methodTrait = \PHP_EOL . ' use \PHPUnit\Framework\MockObject\Method;';
}
$cloneTrait = '';
if ($mockedCloneMethod) {
$cloneTrait = \PHP_EOL . ' use \PHPUnit\Framework\MockObject\MockedCloneMethod;';
}
if ($unmockedCloneMethod) {
$cloneTrait = \PHP_EOL . ' use \PHPUnit\Framework\MockObject\UnmockedCloneMethod;';
}
$classTemplate->setVar(
[
'prologue' => $prologue ?? '',
'epilogue' => $epilogue ?? '',
'class_declaration' => $this->generateMockClassDeclaration(
$mockClassName,
$isInterface,
$additionalInterfaces
),
'clone' => $cloneTrait,
'mock_class_name' => $mockClassName['className'],
'mocked_methods' => $mockedMethods,
'method' => $methodTrait,
]
);
return new MockClass(
$classTemplate->render(),
$mockClassName['className'],
$configurable
);
}
/**
* @param array|string $type
*/
private function generateClassName($type, string $className, string $prefix): array
{
if (\is_array($type)) {
$type = \implode('_', $type);
}
if ($type[0] === '\\') {
$type = \substr($type, 1);
}
$classNameParts = \explode('\\', $type);
if (\count($classNameParts) > 1) {
$type = \array_pop($classNameParts);
$namespaceName = \implode('\\', $classNameParts);
$fullClassName = $namespaceName . '\\' . $type;
} else {
$namespaceName = '';
$fullClassName = $type;
}
if ($className === '') {
do {
$className = $prefix . $type . '_' .
\substr(\md5((string) \mt_rand()), 0, 8);
} while (\class_exists($className, false));
}
return [
'className' => $className,
'originalClassName' => $type,
'fullClassName' => $fullClassName,
'namespaceName' => $namespaceName,
];
}
private function generateMockClassDeclaration(array $mockClassName, bool $isInterface, array $additionalInterfaces = []): string
{
$buffer = 'class ';
$additionalInterfaces[] = MockObject::class;
$interfaces = \implode(', ', $additionalInterfaces);
if ($isInterface) {
$buffer .= \sprintf(
'%s implements %s',
$mockClassName['className'],
$interfaces
);
if (!\in_array($mockClassName['originalClassName'], $additionalInterfaces, true)) {
$buffer .= ', ';
if (!empty($mockClassName['namespaceName'])) {
$buffer .= $mockClassName['namespaceName'] . '\\';
}
$buffer .= $mockClassName['originalClassName'];
}
} else {
$buffer .= \sprintf(
'%s extends %s%s implements %s',
$mockClassName['className'],
!empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
$mockClassName['originalClassName'],
$interfaces
);
}
return $buffer;
}
private function canMockMethod(\ReflectionMethod $method): bool
{
return !($method->isConstructor() || $method->isFinal() || $method->isPrivate() || $this->isMethodNameBlacklisted($method->getName()));
}
private function isMethodNameBlacklisted(string $name): bool
{
return isset(self::BLACKLISTED_METHOD_NAMES[$name]);
}
private function getTemplate(string $template): \Text_Template
{
$filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template;
if (!isset(self::$templates[$filename])) {
self::$templates[$filename] = new \Text_Template($filename);
}
return self::$templates[$filename];
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface MockType
{
public function generate(): string;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\TestCase;
/**
* @psalm-template MockedType
*/
final class MockBuilder
{
/**
* @var TestCase
*/
private $testCase;
/**
* @var string
*/
private $type;
/**
* @var string[]
*/
private $methods = [];
/**
* @var bool
*/
private $emptyMethodsArray = false;
/**
* @var string
*/
private $mockClassName = '';
/**
* @var array
*/
private $constructorArgs = [];
/**
* @var bool
*/
private $originalConstructor = true;
/**
* @var bool
*/
private $originalClone = true;
/**
* @var bool
*/
private $autoload = true;
/**
* @var bool
*/
private $cloneArguments = false;
/**
* @var bool
*/
private $callOriginalMethods = false;
/**
* @var ?object
*/
private $proxyTarget;
/**
* @var bool
*/
private $allowMockingUnknownTypes = true;
/**
* @var bool
*/
private $returnValueGeneration = true;
/**
* @var Generator
*/
private $generator;
/**
* @var bool
*/
private $alreadyUsedMockMethodConfiguration = false;
/**
* @param string|string[] $type
*
* @psalm-param class-string<MockedType>|string|string[] $type
*/
public function __construct(TestCase $testCase, $type)
{
$this->testCase = $testCase;
$this->type = $type;
$this->generator = new Generator;
}
/**
* Creates a mock object using a fluent interface.
*
* @throws RuntimeException
*
* @psalm-return MockObject&MockedType
*/
public function getMock(): MockObject
{
$object = $this->generator->getMock(
$this->type,
!$this->emptyMethodsArray ? $this->methods : null,
$this->constructorArgs,
$this->mockClassName,
$this->originalConstructor,
$this->originalClone,
$this->autoload,
$this->cloneArguments,
$this->callOriginalMethods,
$this->proxyTarget,
$this->allowMockingUnknownTypes,
$this->returnValueGeneration
);
$this->testCase->registerMockObject($object);
return $object;
}
/**
* Creates a mock object for an abstract class using a fluent interface.
*
* @throws \PHPUnit\Framework\Exception
* @throws RuntimeException
*
* @psalm-return MockObject&MockedType
*/
public function getMockForAbstractClass(): MockObject
{
$object = $this->generator->getMockForAbstractClass(
$this->type,
$this->constructorArgs,
$this->mockClassName,
$this->originalConstructor,
$this->originalClone,
$this->autoload,
$this->methods,
$this->cloneArguments
);
$this->testCase->registerMockObject($object);
return $object;
}
/**
* Creates a mock object for a trait using a fluent interface.
*
* @throws \PHPUnit\Framework\Exception
* @throws RuntimeException
*
* @psalm-return MockObject&MockedType
*/
public function getMockForTrait(): MockObject
{
$object = $this->generator->getMockForTrait(
$this->type,
$this->constructorArgs,
$this->mockClassName,
$this->originalConstructor,
$this->originalClone,
$this->autoload,
$this->methods,
$this->cloneArguments
);
$this->testCase->registerMockObject($object);
return $object;
}
/**
* Specifies the subset of methods to mock. Default is to mock none of them.
*
* @deprecated https://github.com/sebastianbergmann/phpunit/pull/3687
*/
public function setMethods(array $methods = null): self
{
$this->methods = $methods;
$this->alreadyUsedMockMethodConfiguration = true;
return $this;
}
/**
* Specifies the subset of methods to mock, requiring each to exist in the class
*
* @param string[] $methods
*
* @throws RuntimeException
*/
public function onlyMethods(array $methods): self
{
if (empty($methods)) {
$this->emptyMethodsArray = true;
return $this;
}
if ($this->alreadyUsedMockMethodConfiguration) {
throw new RuntimeException(
\sprintf(
'Cannot use onlyMethods() on "%s" mock because mocked methods were already configured.',
$this->type
)
);
}
$this->alreadyUsedMockMethodConfiguration = true;
try {
$reflector = new \ReflectionClass($this->type);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
foreach ($methods as $method) {
if (!$reflector->hasMethod($method)) {
throw new RuntimeException(
\sprintf(
'Trying to set mock method "%s" with onlyMethods, but it does not exist in class "%s". Use addMethods() for methods that don\'t exist in the class.',
$method,
$this->type
)
);
}
}
$this->methods = $methods;
return $this;
}
/**
* Specifies methods that don't exist in the class which you want to mock
*
* @param string[] $methods
*
* @throws RuntimeException
*/
public function addMethods(array $methods): self
{
if (empty($methods)) {
$this->emptyMethodsArray = true;
return $this;
}
if ($this->alreadyUsedMockMethodConfiguration) {
throw new RuntimeException(
\sprintf(
'Cannot use addMethods() on "%s" mock because mocked methods were already configured.',
$this->type
)
);
}
$this->alreadyUsedMockMethodConfiguration = true;
try {
$reflector = new \ReflectionClass($this->type);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
foreach ($methods as $method) {
if ($reflector->hasMethod($method)) {
throw new RuntimeException(
\sprintf(
'Trying to set mock method "%s" with addMethods(), but it exists in class "%s". Use onlyMethods() for methods that exist in the class.',
$method,
$this->type
)
);
}
}
$this->methods = $methods;
return $this;
}
/**
* Specifies the subset of methods to not mock. Default is to mock all of them.
*/
public function setMethodsExcept(array $methods = []): self
{
return $this->setMethods(
\array_diff(
$this->generator->getClassMethods($this->type),
$methods
)
);
}
/**
* Specifies the arguments for the constructor.
*/
public function setConstructorArgs(array $args): self
{
$this->constructorArgs = $args;
return $this;
}
/**
* Specifies the name for the mock class.
*/
public function setMockClassName(string $name): self
{
$this->mockClassName = $name;
return $this;
}
/**
* Disables the invocation of the original constructor.
*/
public function disableOriginalConstructor(): self
{
$this->originalConstructor = false;
return $this;
}
/**
* Enables the invocation of the original constructor.
*/
public function enableOriginalConstructor(): self
{
$this->originalConstructor = true;
return $this;
}
/**
* Disables the invocation of the original clone constructor.
*/
public function disableOriginalClone(): self
{
$this->originalClone = false;
return $this;
}
/**
* Enables the invocation of the original clone constructor.
*/
public function enableOriginalClone(): self
{
$this->originalClone = true;
return $this;
}
/**
* Disables the use of class autoloading while creating the mock object.
*/
public function disableAutoload(): self
{
$this->autoload = false;
return $this;
}
/**
* Enables the use of class autoloading while creating the mock object.
*/
public function enableAutoload(): self
{
$this->autoload = true;
return $this;
}
/**
* Disables the cloning of arguments passed to mocked methods.
*/
public function disableArgumentCloning(): self
{
$this->cloneArguments = false;
return $this;
}
/**
* Enables the cloning of arguments passed to mocked methods.
*/
public function enableArgumentCloning(): self
{
$this->cloneArguments = true;
return $this;
}
/**
* Enables the invocation of the original methods.
*/
public function enableProxyingToOriginalMethods(): self
{
$this->callOriginalMethods = true;
return $this;
}
/**
* Disables the invocation of the original methods.
*/
public function disableProxyingToOriginalMethods(): self
{
$this->callOriginalMethods = false;
$this->proxyTarget = null;
return $this;
}
/**
* Sets the proxy target.
*/
public function setProxyTarget(object $object): self
{
$this->proxyTarget = $object;
return $this;
}
public function allowMockingUnknownTypes(): self
{
$this->allowMockingUnknownTypes = true;
return $this;
}
public function disallowMockingUnknownTypes(): self
{
$this->allowMockingUnknownTypes = false;
return $this;
}
public function enableAutoReturnValueGeneration(): self
{
$this->returnValueGeneration = true;
return $this;
}
public function disableAutoReturnValueGeneration(): self
{
$this->returnValueGeneration = false;
return $this;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
/**
* @method BuilderInvocationMocker method($constraint)
*/
interface MockObject extends Stub
{
public function __phpunit_setOriginalObject($originalObject): void;
public function __phpunit_verify(bool $unsetInvocationMocker = true): void;
public function expects(InvocationOrder $invocationRule): BuilderInvocationMocker;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount;
/**
* @internal This trait is not covered by the backward compatibility promise for PHPUnit
*/
trait Method
{
public function method()
{
$expects = $this->expects(new AnyInvokedCount);
return \call_user_func_array(
[$expects, 'method'],
\func_get_args()
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This trait is not covered by the backward compatibility promise for PHPUnit
*/
trait UnmockedCloneMethod
{
public function __clone()
{
$this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler();
parent::__clone();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\MockObject\Builder\InvocationMocker as InvocationMockerBuilder;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
/**
* @internal This trait is not covered by the backward compatibility promise for PHPUnit
*/
trait Api
{
/**
* @var ConfigurableMethod[]
*/
private static $__phpunit_configurableMethods;
/**
* @var @object
*/
private $__phpunit_originalObject;
/**
* @var @bool
*/
private $__phpunit_returnValueGeneration = true;
/**
* @var InvocationHandler
*/
private $__phpunit_invocationMocker;
/** @noinspection MagicMethodsValidityInspection */
public static function __phpunit_initConfigurableMethods(ConfigurableMethod ...$configurableMethods): void
{
if (isset(static::$__phpunit_configurableMethods)) {
throw new ConfigurableMethodsAlreadyInitializedException(
'Configurable methods is already initialized and can not be reinitialized'
);
}
static::$__phpunit_configurableMethods = $configurableMethods;
}
/** @noinspection MagicMethodsValidityInspection */
public function __phpunit_setOriginalObject($originalObject): void
{
$this->__phpunit_originalObject = $originalObject;
}
/** @noinspection MagicMethodsValidityInspection */
public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void
{
$this->__phpunit_returnValueGeneration = $returnValueGeneration;
}
/** @noinspection MagicMethodsValidityInspection */
public function __phpunit_getInvocationHandler(): InvocationHandler
{
if ($this->__phpunit_invocationMocker === null) {
$this->__phpunit_invocationMocker = new InvocationHandler(
static::$__phpunit_configurableMethods,
$this->__phpunit_returnValueGeneration
);
}
return $this->__phpunit_invocationMocker;
}
/** @noinspection MagicMethodsValidityInspection */
public function __phpunit_hasMatchers(): bool
{
return $this->__phpunit_getInvocationHandler()->hasMatchers();
}
/** @noinspection MagicMethodsValidityInspection */
public function __phpunit_verify(bool $unsetInvocationMocker = true): void
{
$this->__phpunit_getInvocationHandler()->verify();
if ($unsetInvocationMocker) {
$this->__phpunit_invocationMocker = null;
}
}
public function expects(InvocationOrder $matcher): InvocationMockerBuilder
{
return $this->__phpunit_getInvocationHandler()->expects($matcher);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This trait is not covered by the backward compatibility promise for PHPUnit
*/
trait MockedCloneMethod
{
public function __clone()
{
$this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationHandler();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use Exception;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Builder\InvocationMocker;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvocationHandler
{
/**
* @var Matcher[]
*/
private $matchers = [];
/**
* @var Matcher[]
*/
private $matcherMap = [];
/**
* @var ConfigurableMethod[]
*/
private $configurableMethods;
/**
* @var bool
*/
private $returnValueGeneration;
/**
* @var \Throwable
*/
private $deferredError;
public function __construct(array $configurableMethods, bool $returnValueGeneration)
{
$this->configurableMethods = $configurableMethods;
$this->returnValueGeneration = $returnValueGeneration;
}
public function hasMatchers(): bool
{
foreach ($this->matchers as $matcher) {
if ($matcher->hasMatchers()) {
return true;
}
}
return false;
}
/**
* Looks up the match builder with identification $id and returns it.
*
* @param string $id The identification of the match builder
*/
public function lookupMatcher(string $id): ?Matcher
{
if (isset($this->matcherMap[$id])) {
return $this->matcherMap[$id];
}
return null;
}
/**
* Registers a matcher with the identification $id. The matcher can later be
* looked up using lookupMatcher() to figure out if it has been invoked.
*
* @param string $id The identification of the matcher
* @param Matcher $matcher The builder which is being registered
*
* @throws RuntimeException
*/
public function registerMatcher(string $id, Matcher $matcher): void
{
if (isset($this->matcherMap[$id])) {
throw new RuntimeException(
'Matcher with id <' . $id . '> is already registered.'
);
}
$this->matcherMap[$id] = $matcher;
}
public function expects(InvocationOrder $rule): InvocationMocker
{
$matcher = new Matcher($rule);
$this->addMatcher($matcher);
return new InvocationMocker(
$this,
$matcher,
...$this->configurableMethods
);
}
/**
* @throws Exception
*
* @return mixed|void
*/
public function invoke(Invocation $invocation)
{
$exception = null;
$hasReturnValue = false;
$returnValue = null;
foreach ($this->matchers as $match) {
try {
if ($match->matches($invocation)) {
$value = $match->invoked($invocation);
if (!$hasReturnValue) {
$returnValue = $value;
$hasReturnValue = true;
}
}
} catch (Exception $e) {
$exception = $e;
}
}
if ($exception !== null) {
throw $exception;
}
if ($hasReturnValue) {
return $returnValue;
}
if (!$this->returnValueGeneration) {
$exception = new ExpectationFailedException(
\sprintf(
'Return value inference disabled and no expectation set up for %s::%s()',
$invocation->getClassName(),
$invocation->getMethodName()
)
);
if (\strtolower($invocation->getMethodName()) === '__tostring') {
$this->deferredError = $exception;
return '';
}
throw $exception;
}
return $invocation->generateReturnValue();
}
public function matches(Invocation $invocation): bool
{
foreach ($this->matchers as $matcher) {
if (!$matcher->matches($invocation)) {
return false;
}
}
return true;
}
/**
* @throws \PHPUnit\Framework\ExpectationFailedException
*/
public function verify(): void
{
foreach ($this->matchers as $matcher) {
$matcher->verify();
}
if ($this->deferredError) {
throw $this->deferredError;
}
}
private function addMatcher(Matcher $matcher): void
{
$this->matchers[] = $matcher;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class IncompatibleReturnValueException extends \PHPUnit\Framework\Exception implements Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class BadMethodCallException extends \BadMethodCallException implements Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ConfigurableMethodsAlreadyInitializedException extends \PHPUnit\Framework\Exception implements Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface Exception extends \Throwable
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class RuntimeException extends \RuntimeException implements Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\ExpectationFailedException;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface Verifiable
{
/**
* Verifies that the current expectation is valid. If everything is OK the
* code should just return, if not it must throw an exception.
*
* @throws ExpectationFailedException
*/
public function verify(): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount;
use PHPUnit\Framework\MockObject\Rule\AnyParameters;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
use PHPUnit\Framework\MockObject\Rule\InvokedCount;
use PHPUnit\Framework\MockObject\Rule\MethodName;
use PHPUnit\Framework\MockObject\Rule\ParametersRule;
use PHPUnit\Framework\MockObject\Stub\Stub;
use PHPUnit\Framework\TestFailure;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Matcher
{
/**
* @var InvocationOrder
*/
private $invocationRule;
/**
* @var mixed
*/
private $afterMatchBuilderId;
/**
* @var bool
*/
private $afterMatchBuilderIsInvoked = false;
/**
* @var MethodName
*/
private $methodNameRule;
/**
* @var ParametersRule
*/
private $parametersRule;
/**
* @var Stub
*/
private $stub;
public function __construct(InvocationOrder $rule)
{
$this->invocationRule = $rule;
}
public function hasMatchers(): bool
{
return !$this->invocationRule instanceof AnyInvokedCount;
}
public function hasMethodNameRule(): bool
{
return $this->methodNameRule !== null;
}
public function getMethodNameRule(): MethodName
{
return $this->methodNameRule;
}
public function setMethodNameRule(MethodName $rule): void
{
$this->methodNameRule = $rule;
}
public function hasParametersRule(): bool
{
return $this->parametersRule !== null;
}
public function setParametersRule(ParametersRule $rule): void
{
$this->parametersRule = $rule;
}
public function setStub(Stub $stub): void
{
$this->stub = $stub;
}
public function setAfterMatchBuilderId(string $id): void
{
$this->afterMatchBuilderId = $id;
}
/**
* @throws \Exception
* @throws RuntimeException
* @throws ExpectationFailedException
*/
public function invoked(Invocation $invocation)
{
if ($this->methodNameRule === null) {
throw new RuntimeException('No method rule is set');
}
if ($this->afterMatchBuilderId !== null) {
$matcher = $invocation->getObject()
->__phpunit_getInvocationHandler()
->lookupMatcher($this->afterMatchBuilderId);
if (!$matcher) {
throw new RuntimeException(
\sprintf(
'No builder found for match builder identification <%s>',
$this->afterMatchBuilderId
)
);
}
\assert($matcher instanceof self);
if ($matcher->invocationRule->hasBeenInvoked()) {
$this->afterMatchBuilderIsInvoked = true;
}
}
$this->invocationRule->invoked($invocation);
try {
if ($this->parametersRule !== null) {
$this->parametersRule->apply($invocation);
}
} catch (ExpectationFailedException $e) {
throw new ExpectationFailedException(
\sprintf(
"Expectation failed for %s when %s\n%s",
$this->methodNameRule->toString(),
$this->invocationRule->toString(),
$e->getMessage()
),
$e->getComparisonFailure()
);
}
if ($this->stub) {
return $this->stub->invoke($invocation);
}
return $invocation->generateReturnValue();
}
/**
* @throws RuntimeException
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function matches(Invocation $invocation): bool
{
if ($this->afterMatchBuilderId !== null) {
$matcher = $invocation->getObject()
->__phpunit_getInvocationHandler()
->lookupMatcher($this->afterMatchBuilderId);
if (!$matcher) {
throw new RuntimeException(
\sprintf(
'No builder found for match builder identification <%s>',
$this->afterMatchBuilderId
)
);
}
\assert($matcher instanceof self);
if (!$matcher->invocationRule->hasBeenInvoked()) {
return false;
}
}
if ($this->methodNameRule === null) {
throw new RuntimeException('No method rule is set');
}
if (!$this->invocationRule->matches($invocation)) {
return false;
}
try {
if (!$this->methodNameRule->matches($invocation)) {
return false;
}
} catch (ExpectationFailedException $e) {
throw new ExpectationFailedException(
\sprintf(
"Expectation failed for %s when %s\n%s",
$this->methodNameRule->toString(),
$this->invocationRule->toString(),
$e->getMessage()
),
$e->getComparisonFailure()
);
}
return true;
}
/**
* @throws RuntimeException
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function verify(): void
{
if ($this->methodNameRule === null) {
throw new RuntimeException('No method rule is set');
}
try {
$this->invocationRule->verify();
if ($this->parametersRule === null) {
$this->parametersRule = new AnyParameters;
}
$invocationIsAny = $this->invocationRule instanceof AnyInvokedCount;
$invocationIsNever = $this->invocationRule instanceof InvokedCount && $this->invocationRule->isNever();
if (!$invocationIsAny && !$invocationIsNever) {
$this->parametersRule->verify();
}
} catch (ExpectationFailedException $e) {
throw new ExpectationFailedException(
\sprintf(
"Expectation failed for %s when %s.\n%s",
$this->methodNameRule->toString(),
$this->invocationRule->toString(),
TestFailure::exceptionToString($e)
)
);
}
}
public function toString(): string
{
$list = [];
if ($this->invocationRule !== null) {
$list[] = $this->invocationRule->toString();
}
if ($this->methodNameRule !== null) {
$list[] = 'where ' . $this->methodNameRule->toString();
}
if ($this->parametersRule !== null) {
$list[] = 'and ' . $this->parametersRule->toString();
}
if ($this->afterMatchBuilderId !== null) {
$list[] = 'after ' . $this->afterMatchBuilderId;
}
if ($this->stub !== null) {
$list[] = 'will ' . $this->stub->toString();
}
return \implode(' ', $list);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\Constraint\Constraint;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MethodNameConstraint extends Constraint
{
/**
* @var string
*/
private $methodName;
public function __construct(string $methodName)
{
$this->methodName = $methodName;
}
public function toString(): string
{
return \sprintf(
'is "%s"',
$this->methodName
);
}
protected function matches($other): bool
{
if (!\is_string($other)) {
return false;
}
return \strtolower($this->methodName) === \strtolower($other);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ReturnValueMap implements Stub
{
/**
* @var array
*/
private $valueMap;
public function __construct(array $valueMap)
{
$this->valueMap = $valueMap;
}
public function invoke(Invocation $invocation)
{
$parameterCount = \count($invocation->getParameters());
foreach ($this->valueMap as $map) {
if (!\is_array($map) || $parameterCount !== (\count($map) - 1)) {
continue;
}
$return = \array_pop($map);
if ($invocation->getParameters() === $map) {
return $return;
}
}
}
public function toString(): string
{
return 'return value from a map';
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
use SebastianBergmann\Exporter\Exporter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ConsecutiveCalls implements Stub
{
/**
* @var array
*/
private $stack;
/**
* @var mixed
*/
private $value;
public function __construct(array $stack)
{
$this->stack = $stack;
}
public function invoke(Invocation $invocation)
{
$this->value = \array_shift($this->stack);
if ($this->value instanceof Stub) {
$this->value = $this->value->invoke($invocation);
}
return $this->value;
}
public function toString(): string
{
$exporter = new Exporter;
return \sprintf(
'return user-specified value %s',
$exporter->export($this->value)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
use SebastianBergmann\Exporter\Exporter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Exception implements Stub
{
private $exception;
public function __construct(\Throwable $exception)
{
$this->exception = $exception;
}
/**
* @throws \Throwable
*/
public function invoke(Invocation $invocation): void
{
throw $this->exception;
}
public function toString(): string
{
$exporter = new Exporter;
return \sprintf(
'raise user-specified exception %s',
$exporter->export($this->exception)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
use PHPUnit\Framework\SelfDescribing;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface Stub extends SelfDescribing
{
/**
* Fakes the processing of the invocation $invocation by returning a
* specific value.
*
* @param Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers
*/
public function invoke(Invocation $invocation);
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
use SebastianBergmann\Exporter\Exporter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ReturnReference implements Stub
{
/**
* @var mixed
*/
private $reference;
public function __construct(&$reference)
{
$this->reference = &$reference;
}
public function invoke(Invocation $invocation)
{
return $this->reference;
}
public function toString(): string
{
$exporter = new Exporter;
return \sprintf(
'return user-specified reference %s',
$exporter->export($this->reference)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ReturnCallback implements Stub
{
private $callback;
public function __construct($callback)
{
$this->callback = $callback;
}
public function invoke(Invocation $invocation)
{
return \call_user_func_array($this->callback, $invocation->getParameters());
}
public function toString(): string
{
if (\is_array($this->callback)) {
if (\is_object($this->callback[0])) {
$class = \get_class($this->callback[0]);
$type = '->';
} else {
$class = $this->callback[0];
$type = '::';
}
return \sprintf(
'return result of user defined callback %s%s%s() with the ' .
'passed arguments',
$class,
$type,
$this->callback[1]
);
}
return 'return result of user defined callback ' . $this->callback .
' with the passed arguments';
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ReturnArgument implements Stub
{
/**
* @var int
*/
private $argumentIndex;
public function __construct($argumentIndex)
{
$this->argumentIndex = $argumentIndex;
}
public function invoke(Invocation $invocation)
{
if (isset($invocation->getParameters()[$this->argumentIndex])) {
return $invocation->getParameters()[$this->argumentIndex];
}
}
public function toString(): string
{
return \sprintf('return argument #%d', $this->argumentIndex);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
use PHPUnit\Framework\MockObject\RuntimeException;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ReturnSelf implements Stub
{
/**
* @throws RuntimeException
*/
public function invoke(Invocation $invocation)
{
return $invocation->getObject();
}
public function toString(): string
{
return 'return the current object';
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Invocation;
use SebastianBergmann\Exporter\Exporter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ReturnStub implements Stub
{
/**
* @var mixed
*/
private $value;
public function __construct($value)
{
$this->value = $value;
}
public function invoke(Invocation $invocation)
{
return $this->value;
}
public function toString(): string
{
$exporter = new Exporter;
return \sprintf(
'return user-specified value %s',
$exporter->export($this->value)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\MockObject\Builder\InvocationStubber;
/**
* @method InvocationStubber method($constraint)
*/
interface Stub
{
public function __phpunit_getInvocationHandler(): InvocationHandler;
public function __phpunit_hasMatchers(): bool;
public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MockClass implements MockType
{
/**
* @var string
*/
private $classCode;
/**
* @var string
*/
private $mockName;
/**
* @var ConfigurableMethod[]
*/
private $configurableMethods;
public function __construct(string $classCode, string $mockName, array $configurableMethods)
{
$this->classCode = $classCode;
$this->mockName = $mockName;
$this->configurableMethods = $configurableMethods;
}
public function generate(): string
{
if (!\class_exists($this->mockName, false)) {
eval($this->classCode);
\call_user_func(
[
$this->mockName,
'__phpunit_initConfigurableMethods',
],
...$this->configurableMethods
);
}
return $this->mockName;
}
public function getClassCode(): string
{
return $this->classCode;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Util\Type;
use SebastianBergmann\Exporter\Exporter;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Invocation implements SelfDescribing
{
/**
* @var string
*/
private $className;
/**
* @var string
*/
private $methodName;
/**
* @var array
*/
private $parameters;
/**
* @var string
*/
private $returnType;
/**
* @var bool
*/
private $isReturnTypeNullable = false;
/**
* @var bool
*/
private $proxiedCall;
/**
* @var object
*/
private $object;
public function __construct(string $className, string $methodName, array $parameters, string $returnType, object $object, bool $cloneObjects = false, bool $proxiedCall = false)
{
$this->className = $className;
$this->methodName = $methodName;
$this->parameters = $parameters;
$this->object = $object;
$this->proxiedCall = $proxiedCall;
$returnType = \ltrim($returnType, ': ');
if (\strtolower($methodName) === '__tostring') {
$returnType = 'string';
}
if (\strpos($returnType, '?') === 0) {
$returnType = \substr($returnType, 1);
$this->isReturnTypeNullable = true;
}
$this->returnType = $returnType;
if (!$cloneObjects) {
return;
}
foreach ($this->parameters as $key => $value) {
if (\is_object($value)) {
$this->parameters[$key] = $this->cloneObject($value);
}
}
}
public function getClassName(): string
{
return $this->className;
}
public function getMethodName(): string
{
return $this->methodName;
}
public function getParameters(): array
{
return $this->parameters;
}
/**
* @throws RuntimeException
*
* @return mixed Mocked return value
*/
public function generateReturnValue()
{
if ($this->isReturnTypeNullable || $this->proxiedCall) {
return;
}
switch (\strtolower($this->returnType)) {
case '':
case 'void':
return;
case 'string':
return '';
case 'float':
return 0.0;
case 'int':
return 0;
case 'bool':
return false;
case 'array':
return [];
case 'object':
return new \stdClass;
case 'callable':
case 'closure':
return function (): void {
};
case 'traversable':
case 'generator':
case 'iterable':
$generator = static function () {
yield;
};
return $generator();
default:
$generator = new Generator;
return $generator->getMock($this->returnType, [], [], '', false);
}
}
public function toString(): string
{
$exporter = new Exporter;
return \sprintf(
'%s::%s(%s)%s',
$this->className,
$this->methodName,
\implode(
', ',
\array_map(
[$exporter, 'shortenedExport'],
$this->parameters
)
),
$this->returnType ? \sprintf(': %s', $this->returnType) : ''
);
}
public function getObject(): object
{
return $this->object;
}
private function cloneObject(object $original): object
{
if (Type::isCloneable($original)) {
return clone $original;
}
return $original;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use SebastianBergmann\Type\Type;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ConfigurableMethod
{
/**
* @var string
*/
private $name;
/**
* @var Type
*/
private $returnType;
public function __construct(string $name, Type $returnType)
{
$this->name = $name;
$this->returnType = $returnType;
}
public function getName(): string
{
return $this->name;
}
public function mayReturn($value): bool
{
if ($value === null && $this->returnType->allowsNull()) {
return true;
}
return $this->returnType->isAssignable(Type::fromValue($value, false));
}
public function getReturnTypeDeclaration(): string
{
return $this->returnType->getReturnTypeDeclaration();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class AnyInvokedCount extends InvocationOrder
{
public function toString(): string
{
return 'invoked zero or more times';
}
public function verify(): void
{
}
public function matches(BaseInvocation $invocation): bool
{
return true;
}
protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvokedAtLeastOnce extends InvocationOrder
{
public function toString(): string
{
return 'invoked at least once';
}
/**
* Verifies that the current expectation is valid. If everything is OK the
* code should just return, if not it must throw an exception.
*
* @throws ExpectationFailedException
*/
public function verify(): void
{
$count = $this->getInvocationCount();
if ($count < 1) {
throw new ExpectationFailedException(
'Expected invocation at least once but it never occurred.'
);
}
}
public function matches(BaseInvocation $invocation): bool
{
return true;
}
protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Constraint\IsAnything;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Parameters implements ParametersRule
{
/**
* @var Constraint[]
*/
private $parameters = [];
/**
* @var BaseInvocation
*/
private $invocation;
/**
* @var bool|ExpectationFailedException
*/
private $parameterVerificationResult;
/**
* @throws \PHPUnit\Framework\Exception
*/
public function __construct(array $parameters)
{
foreach ($parameters as $parameter) {
if (!($parameter instanceof Constraint)) {
$parameter = new IsEqual(
$parameter
);
}
$this->parameters[] = $parameter;
}
}
public function toString(): string
{
$text = 'with parameter';
foreach ($this->parameters as $index => $parameter) {
if ($index > 0) {
$text .= ' and';
}
$text .= ' ' . $index . ' ' . $parameter->toString();
}
return $text;
}
/**
* @throws \Exception
*/
public function apply(BaseInvocation $invocation): void
{
$this->invocation = $invocation;
$this->parameterVerificationResult = null;
try {
$this->parameterVerificationResult = $this->doVerify();
} catch (ExpectationFailedException $e) {
$this->parameterVerificationResult = $e;
throw $this->parameterVerificationResult;
}
}
/**
* Checks if the invocation $invocation matches the current rules. If it
* does the rule will get the invoked() method called which should check
* if an expectation is met.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function verify(): void
{
$this->doVerify();
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function doVerify(): bool
{
if (isset($this->parameterVerificationResult)) {
return $this->guardAgainstDuplicateEvaluationOfParameterConstraints();
}
if ($this->invocation === null) {
throw new ExpectationFailedException('Mocked method does not exist.');
}
if (\count($this->invocation->getParameters()) < \count($this->parameters)) {
$message = 'Parameter count for invocation %s is too low.';
// The user called `->with($this->anything())`, but may have meant
// `->withAnyParameters()`.
//
// @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/199
if (\count($this->parameters) === 1 &&
\get_class($this->parameters[0]) === IsAnything::class) {
$message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead.";
}
throw new ExpectationFailedException(
\sprintf($message, $this->invocation->toString())
);
}
foreach ($this->parameters as $i => $parameter) {
$parameter->evaluate(
$this->invocation->getParameters()[$i],
\sprintf(
'Parameter %s for invocation %s does not match expected ' .
'value.',
$i,
$this->invocation->toString()
)
);
}
return true;
}
/**
* @throws ExpectationFailedException
*/
private function guardAgainstDuplicateEvaluationOfParameterConstraints(): bool
{
if ($this->parameterVerificationResult instanceof ExpectationFailedException) {
throw $this->parameterVerificationResult;
}
return (bool) $this->parameterVerificationResult;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvokedAtLeastCount extends InvocationOrder
{
/**
* @var int
*/
private $requiredInvocations;
/**
* @param int $requiredInvocations
*/
public function __construct($requiredInvocations)
{
$this->requiredInvocations = $requiredInvocations;
}
public function toString(): string
{
return 'invoked at least ' . $this->requiredInvocations . ' times';
}
/**
* Verifies that the current expectation is valid. If everything is OK the
* code should just return, if not it must throw an exception.
*
* @throws ExpectationFailedException
*/
public function verify(): void
{
$count = $this->getInvocationCount();
if ($count < $this->requiredInvocations) {
throw new ExpectationFailedException(
'Expected invocation at least ' . $this->requiredInvocations .
' times but it occurred ' . $count . ' time(s).'
);
}
}
public function matches(BaseInvocation $invocation): bool
{
return true;
}
protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
class InvokedAtIndex extends InvocationOrder
{
/**
* @var int
*/
private $sequenceIndex;
/**
* @var int
*/
private $currentIndex = -1;
/**
* @param int $sequenceIndex
*/
public function __construct($sequenceIndex)
{
$this->sequenceIndex = $sequenceIndex;
}
public function toString(): string
{
return 'invoked at sequence index ' . $this->sequenceIndex;
}
public function matches(BaseInvocation $invocation): bool
{
$this->currentIndex++;
return $this->currentIndex == $this->sequenceIndex;
}
/**
* Verifies that the current expectation is valid. If everything is OK the
* code should just return, if not it must throw an exception.
*
* @throws ExpectationFailedException
*/
public function verify(): void
{
if ($this->currentIndex < $this->sequenceIndex) {
throw new ExpectationFailedException(
\sprintf(
'The expected invocation at index %s was never reached.',
$this->sequenceIndex
)
);
}
}
protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
use PHPUnit\Framework\MockObject\Verifiable;
use PHPUnit\Framework\SelfDescribing;
interface ParametersRule extends SelfDescribing, Verifiable
{
/**
* @throws ExpectationFailedException if the invocation violates the rule
*/
public function apply(BaseInvocation $invocation): void;
public function verify(): void;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class AnyParameters implements ParametersRule
{
public function toString(): string
{
return 'with any parameters';
}
public function apply(BaseInvocation $invocation): void
{
}
public function verify(): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvokedCount extends InvocationOrder
{
/**
* @var int
*/
private $expectedCount;
/**
* @param int $expectedCount
*/
public function __construct($expectedCount)
{
$this->expectedCount = $expectedCount;
}
public function isNever(): bool
{
return $this->expectedCount === 0;
}
public function toString(): string
{
return 'invoked ' . $this->expectedCount . ' time(s)';
}
public function matches(BaseInvocation $invocation): bool
{
return true;
}
/**
* Verifies that the current expectation is valid. If everything is OK the
* code should just return, if not it must throw an exception.
*
* @throws ExpectationFailedException
*/
public function verify(): void
{
$count = $this->getInvocationCount();
if ($count !== $this->expectedCount) {
throw new ExpectationFailedException(
\sprintf(
'Method was expected to be called %d times, ' .
'actually called %d times.',
$this->expectedCount,
$count
)
);
}
}
/**
* @throws ExpectationFailedException
*/
protected function invokedDo(BaseInvocation $invocation): void
{
$count = $this->getInvocationCount();
if ($count > $this->expectedCount) {
$message = $invocation->toString() . ' ';
switch ($this->expectedCount) {
case 0:
$message .= 'was not expected to be called.';
break;
case 1:
$message .= 'was not expected to be called more than once.';
break;
default:
$message .= \sprintf(
'was not expected to be called more than %d times.',
$this->expectedCount
);
}
throw new ExpectationFailedException($message);
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\InvalidArgumentException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
use PHPUnit\Framework\MockObject\MethodNameConstraint;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MethodName
{
/**
* @var Constraint
*/
private $constraint;
/**
* @param Constraint|string
*
* @throws Constraint
* @throws \PHPUnit\Framework\Exception
*/
public function __construct($constraint)
{
if (!$constraint instanceof Constraint) {
if (!\is_string($constraint)) {
throw InvalidArgumentException::create(1, 'string');
}
$constraint = new MethodNameConstraint($constraint);
}
$this->constraint = $constraint;
}
public function toString(): string
{
return 'method name ' . $this->constraint->toString();
}
/**
* @throws \PHPUnit\Framework\ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function matches(BaseInvocation $invocation): bool
{
return $this->matchesName($invocation->getMethodName());
}
public function matchesName(string $methodName): bool
{
return $this->constraint->evaluate($methodName, '', true);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
use PHPUnit\Framework\MockObject\Verifiable;
use PHPUnit\Framework\SelfDescribing;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
abstract class InvocationOrder implements SelfDescribing, Verifiable
{
/**
* @var BaseInvocation[]
*/
private $invocations = [];
public function getInvocationCount(): int
{
return \count($this->invocations);
}
public function hasBeenInvoked(): bool
{
return \count($this->invocations) > 0;
}
final public function invoked(BaseInvocation $invocation)
{
$this->invocations[] = $invocation;
return $this->invokedDo($invocation);
}
abstract public function matches(BaseInvocation $invocation): bool;
abstract protected function invokedDo(BaseInvocation $invocation);
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvokedAtMostCount extends InvocationOrder
{
/**
* @var int
*/
private $allowedInvocations;
/**
* @param int $allowedInvocations
*/
public function __construct($allowedInvocations)
{
$this->allowedInvocations = $allowedInvocations;
}
public function toString(): string
{
return 'invoked at most ' . $this->allowedInvocations . ' times';
}
/**
* Verifies that the current expectation is valid. If everything is OK the
* code should just return, if not it must throw an exception.
*
* @throws ExpectationFailedException
*/
public function verify(): void
{
$count = $this->getInvocationCount();
if ($count > $this->allowedInvocations) {
throw new ExpectationFailedException(
'Expected invocation at most ' . $this->allowedInvocations .
' times but it occurred ' . $count . ' time(s).'
);
}
}
public function matches(BaseInvocation $invocation): bool
{
return true;
}
protected function invokedDo(BaseInvocation $invocation): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\InvalidParameterGroupException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ConsecutiveParameters implements ParametersRule
{
/**
* @var array
*/
private $parameterGroups = [];
/**
* @var array
*/
private $invocations = [];
/**
* @throws \PHPUnit\Framework\Exception
*/
public function __construct(array $parameterGroups)
{
foreach ($parameterGroups as $index => $parameters) {
if (!\is_iterable($parameters)) {
throw new InvalidParameterGroupException(
\sprintf(
'Parameter group #%d must be an array or Traversable, got %s',
$index,
\gettype($parameters)
)
);
}
foreach ($parameters as $parameter) {
if (!$parameter instanceof Constraint) {
$parameter = new IsEqual($parameter);
}
$this->parameterGroups[$index][] = $parameter;
}
}
}
public function toString(): string
{
return 'with consecutive parameters';
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function apply(BaseInvocation $invocation): void
{
$this->invocations[] = $invocation;
$callIndex = \count($this->invocations) - 1;
$this->verifyInvocation($invocation, $callIndex);
}
/**
* @throws \PHPUnit\Framework\ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function verify(): void
{
foreach ($this->invocations as $callIndex => $invocation) {
$this->verifyInvocation($invocation, $callIndex);
}
}
/**
* Verify a single invocation
*
* @param int $callIndex
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function verifyInvocation(BaseInvocation $invocation, $callIndex): void
{
if (!isset($this->parameterGroups[$callIndex])) {
// no parameter assertion for this call index
return;
}
if ($invocation === null) {
throw new ExpectationFailedException(
'Mocked method does not exist.'
);
}
$parameters = $this->parameterGroups[$callIndex];
if (\count($invocation->getParameters()) < \count($parameters)) {
throw new ExpectationFailedException(
\sprintf(
'Parameter count for invocation %s is too low.',
$invocation->toString()
)
);
}
foreach ($parameters as $i => $parameter) {
$parameter->evaluate(
$invocation->getParameters()[$i],
\sprintf(
'Parameter %s for invocation #%d %s does not match expected ' .
'value.',
$i,
$callIndex,
$invocation->toString()
)
);
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Builder;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface Identity
{
/**
* Sets the identification of the expectation to $id.
*
* @note The identifier is unique per mock object.
*
* @param string $id unique identification of expectation
*/
public function id($id);
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Builder;
use PHPUnit\Framework\MockObject\Stub\Stub as BaseStub;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface Stub extends Identity
{
/**
* Stubs the matching method with the stub object $stub. Any invocations of
* the matched method will now be handled by the stub instead.
*/
public function will(BaseStub $stub): Identity;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Builder;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface Match extends Stub
{
/**
* Defines the expectation which must occur before the current is valid.
*
* @param string $id the identification of the expectation that should
* occur before this one
*
* @return Stub
*/
public function after($id);
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Builder;
use PHPUnit\Framework\MockObject\Rule\AnyParameters;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface ParametersMatch extends Match
{
/**
* Sets the parameters to match for, each parameter to this function will
* be part of match. To perform specific matches or constraints create a
* new PHPUnit\Framework\Constraint\Constraint and use it for the parameter.
* If the parameter value is not a constraint it will use the
* PHPUnit\Framework\Constraint\IsEqual for the value.
*
* Some examples:
* <code>
* // match first parameter with value 2
* $b->with(2);
* // match first parameter with value 'smock' and second identical to 42
* $b->with('smock', new PHPUnit\Framework\Constraint\IsEqual(42));
* </code>
*
* @return ParametersMatch
*/
public function with(...$arguments);
/**
* Sets a rule which allows any kind of parameters.
*
* Some examples:
* <code>
* // match any number of parameters
* $b->withAnyParameters();
* </code>
*
* @return AnyParameters
*/
public function withAnyParameters();
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Builder;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\MockObject\ConfigurableMethod;
use PHPUnit\Framework\MockObject\IncompatibleReturnValueException;
use PHPUnit\Framework\MockObject\InvocationHandler;
use PHPUnit\Framework\MockObject\Matcher;
use PHPUnit\Framework\MockObject\Rule;
use PHPUnit\Framework\MockObject\RuntimeException;
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls;
use PHPUnit\Framework\MockObject\Stub\Exception;
use PHPUnit\Framework\MockObject\Stub\ReturnArgument;
use PHPUnit\Framework\MockObject\Stub\ReturnCallback;
use PHPUnit\Framework\MockObject\Stub\ReturnReference;
use PHPUnit\Framework\MockObject\Stub\ReturnSelf;
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
use PHPUnit\Framework\MockObject\Stub\ReturnValueMap;
use PHPUnit\Framework\MockObject\Stub\Stub;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvocationMocker implements InvocationStubber, MethodNameMatch
{
/**
* @var InvocationHandler
*/
private $invocationHandler;
/**
* @var Matcher
*/
private $matcher;
/**
* @var ConfigurableMethod[]
*/
private $configurableMethods;
public function __construct(InvocationHandler $handler, Matcher $matcher, ConfigurableMethod ...$configurableMethods)
{
$this->invocationHandler = $handler;
$this->matcher = $matcher;
$this->configurableMethods = $configurableMethods;
}
public function id($id): self
{
$this->invocationHandler->registerMatcher($id, $this->matcher);
return $this;
}
public function will(Stub $stub): Identity
{
$this->matcher->setStub($stub);
return $this;
}
public function willReturn($value, ...$nextValues): self
{
if (\count($nextValues) === 0) {
$this->ensureTypeOfReturnValues([$value]);
$stub = new ReturnStub($value);
} else {
$values = \array_merge([$value], $nextValues);
$this->ensureTypeOfReturnValues($values);
$stub = new ConsecutiveCalls($values);
}
return $this->will($stub);
}
/** {@inheritDoc} */
public function willReturnReference(&$reference): self
{
$stub = new ReturnReference($reference);
return $this->will($stub);
}
public function willReturnMap(array $valueMap): self
{
$stub = new ReturnValueMap($valueMap);
return $this->will($stub);
}
public function willReturnArgument($argumentIndex): self
{
$stub = new ReturnArgument($argumentIndex);
return $this->will($stub);
}
/** {@inheritDoc} */
public function willReturnCallback($callback): self
{
$stub = new ReturnCallback($callback);
return $this->will($stub);
}
public function willReturnSelf(): self
{
$stub = new ReturnSelf;
return $this->will($stub);
}
public function willReturnOnConsecutiveCalls(...$values): self
{
$stub = new ConsecutiveCalls($values);
return $this->will($stub);
}
public function willThrowException(\Throwable $exception): self
{
$stub = new Exception($exception);
return $this->will($stub);
}
public function after($id): self
{
$this->matcher->setAfterMatchBuilderId($id);
return $this;
}
/**
* @throws RuntimeException
*/
public function with(...$arguments): self
{
$this->canDefineParameters();
$this->matcher->setParametersRule(new Rule\Parameters($arguments));
return $this;
}
/**
* @param array ...$arguments
*
* @throws RuntimeException
*/
public function withConsecutive(...$arguments): self
{
$this->canDefineParameters();
$this->matcher->setParametersRule(new Rule\ConsecutiveParameters($arguments));
return $this;
}
/**
* @throws RuntimeException
*/
public function withAnyParameters(): self
{
$this->canDefineParameters();
$this->matcher->setParametersRule(new Rule\AnyParameters);
return $this;
}
/**
* @param Constraint|string $constraint
*
* @throws RuntimeException
*/
public function method($constraint): self
{
if ($this->matcher->hasMethodNameRule()) {
throw new RuntimeException(
'Rule for method name is already defined, cannot redefine'
);
}
$configurableMethodNames = \array_map(
static function (ConfigurableMethod $configurable) {
return \strtolower($configurable->getName());
},
$this->configurableMethods
);
if (\is_string($constraint) && !\in_array(\strtolower($constraint), $configurableMethodNames, true)) {
throw new RuntimeException(
\sprintf(
'Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static',
$constraint
)
);
}
$this->matcher->setMethodNameRule(new Rule\MethodName($constraint));
return $this;
}
/**
* Validate that a parameters rule can be defined, throw exceptions otherwise.
*
* @throws RuntimeException
*/
private function canDefineParameters(): void
{
if (!$this->matcher->hasMethodNameRule()) {
throw new RuntimeException(
'Rule for method name is not defined, cannot define rule for parameters ' .
'without one'
);
}
if ($this->matcher->hasParametersRule()) {
throw new RuntimeException(
'Rule for parameters is already defined, cannot redefine'
);
}
}
private function getConfiguredMethod(): ?ConfigurableMethod
{
$configuredMethod = null;
foreach ($this->configurableMethods as $configurableMethod) {
if ($this->matcher->getMethodNameRule()->matchesName($configurableMethod->getName())) {
if ($configuredMethod !== null) {
return null;
}
$configuredMethod = $configurableMethod;
}
}
return $configuredMethod;
}
private function ensureTypeOfReturnValues(array $values): void
{
$configuredMethod = $this->getConfiguredMethod();
if ($configuredMethod === null) {
return;
}
foreach ($values as $value) {
if (!$configuredMethod->mayReturn($value)) {
throw new IncompatibleReturnValueException(
\sprintf(
'Method %s may not return value of type %s, its return declaration is "%s"',
$configuredMethod->getName(),
\is_object($value) ? \get_class($value) : \gettype($value),
$configuredMethod->getReturnTypeDeclaration()
)
);
}
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Builder;
use PHPUnit\Framework\MockObject\Stub\Stub;
interface InvocationStubber
{
public function will(Stub $stub): Identity;
/** @return self */
public function willReturn($value, ...$nextValues)/*: self */;
/**
* @param mixed $reference
*
* @return self
*/
public function willReturnReference(&$reference)/*: self */;
/**
* @param array<int, array<int, mixed>> $valueMap
*
* @return self
*/
public function willReturnMap(array $valueMap)/*: self */;
/**
* @param int $argumentIndex
*
* @return self
*/
public function willReturnArgument($argumentIndex)/*: self */;
/**
* @param callable $callback
*
* @return self
*/
public function willReturnCallback($callback)/*: self */;
/** @return self */
public function willReturnSelf()/*: self */;
/**
* @param mixed $values
*
* @return self
*/
public function willReturnOnConsecutiveCalls(...$values)/*: self */;
/** @return self */
public function willThrowException(\Throwable $exception)/*: self */;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Builder;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface MethodNameMatch extends ParametersMatch
{
/**
* Adds a new method name match and returns the parameter match object for
* further matching possibilities.
*
* @param \PHPUnit\Framework\Constraint\Constraint $name Constraint for matching method, if a string is passed it will use the PHPUnit_Framework_Constraint_IsEqual
*
* @return ParametersMatch
*/
public function method($name);
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MockTrait implements MockType
{
/**
* @var string
*/
private $classCode;
/**
* @var string
*/
private $mockName;
public function __construct(string $classCode, string $mockName)
{
$this->classCode = $classCode;
$this->mockName = $mockName;
}
public function generate(): string
{
if (!\class_exists($this->mockName, false)) {
eval($this->classCode);
}
return $this->mockName;
}
public function getClassCode(): string
{
return $this->classCode;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MockMethodSet
{
/**
* @var MockMethod[]
*/
private $methods = [];
public function addMethods(MockMethod ...$methods): void
{
foreach ($methods as $method) {
$this->methods[\strtolower($method->getName())] = $method;
}
}
/**
* @return MockMethod[]
*/
public function asArray(): array
{
return \array_values($this->methods);
}
public function hasMethod(string $methodName): bool
{
return \array_key_exists(\strtolower($methodName), $this->methods);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;
use SebastianBergmann\Type\ObjectType;
use SebastianBergmann\Type\Type;
use SebastianBergmann\Type\UnknownType;
use SebastianBergmann\Type\VoidType;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class MockMethod
{
/**
* @var \Text_Template[]
*/
private static $templates = [];
/**
* @var string
*/
private $className;
/**
* @var string
*/
private $methodName;
/**
* @var bool
*/
private $cloneArguments;
/**
* @var string string
*/
private $modifier;
/**
* @var string
*/
private $argumentsForDeclaration;
/**
* @var string
*/
private $argumentsForCall;
/**
* @var Type
*/
private $returnType;
/**
* @var string
*/
private $reference;
/**
* @var bool
*/
private $callOriginalMethod;
/**
* @var bool
*/
private $static;
/**
* @var ?string
*/
private $deprecation;
/**
* @var bool
*/
private $allowsReturnNull;
/**
* @throws RuntimeException
*/
public static function fromReflection(\ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self
{
if ($method->isPrivate()) {
$modifier = 'private';
} elseif ($method->isProtected()) {
$modifier = 'protected';
} else {
$modifier = 'public';
}
if ($method->isStatic()) {
$modifier .= ' static';
}
if ($method->returnsReference()) {
$reference = '&';
} else {
$reference = '';
}
$docComment = $method->getDocComment();
if (\is_string($docComment) &&
\preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation)) {
$deprecation = \trim(\preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1]));
} else {
$deprecation = null;
}
return new self(
$method->getDeclaringClass()->getName(),
$method->getName(),
$cloneArguments,
$modifier,
self::getMethodParameters($method),
self::getMethodParameters($method, true),
self::deriveReturnType($method),
$reference,
$callOriginalMethod,
$method->isStatic(),
$deprecation,
$method->hasReturnType() && $method->getReturnType()->allowsNull()
);
}
public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments): self
{
return new self(
$fullClassName,
$methodName,
$cloneArguments,
'public',
'',
'',
new UnknownType,
'',
false,
false,
null,
false
);
}
public function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, Type $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation, bool $allowsReturnNull)
{
$this->className = $className;
$this->methodName = $methodName;
$this->cloneArguments = $cloneArguments;
$this->modifier = $modifier;
$this->argumentsForDeclaration = $argumentsForDeclaration;
$this->argumentsForCall = $argumentsForCall;
$this->returnType = $returnType;
$this->reference = $reference;
$this->callOriginalMethod = $callOriginalMethod;
$this->static = $static;
$this->deprecation = $deprecation;
$this->allowsReturnNull = $allowsReturnNull;
}
public function getName(): string
{
return $this->methodName;
}
/**
* @throws RuntimeException
*/
public function generateCode(): string
{
if ($this->static) {
$templateFile = 'mocked_static_method.tpl';
} elseif ($this->returnType instanceof VoidType) {
$templateFile = \sprintf(
'%s_method_void.tpl',
$this->callOriginalMethod ? 'proxied' : 'mocked'
);
} else {
$templateFile = \sprintf(
'%s_method.tpl',
$this->callOriginalMethod ? 'proxied' : 'mocked'
);
}
$deprecation = $this->deprecation;
if (null !== $this->deprecation) {
$deprecation = "The $this->className::$this->methodName method is deprecated ($this->deprecation).";
$deprecationTemplate = $this->getTemplate('deprecation.tpl');
$deprecationTemplate->setVar([
'deprecation' => \var_export($deprecation, true),
]);
$deprecation = $deprecationTemplate->render();
}
$template = $this->getTemplate($templateFile);
$template->setVar(
[
'arguments_decl' => $this->argumentsForDeclaration,
'arguments_call' => $this->argumentsForCall,
'return_declaration' => $this->returnType->getReturnTypeDeclaration(),
'arguments_count' => !empty($this->argumentsForCall) ? \substr_count($this->argumentsForCall, ',') + 1 : 0,
'class_name' => $this->className,
'method_name' => $this->methodName,
'modifier' => $this->modifier,
'reference' => $this->reference,
'clone_arguments' => $this->cloneArguments ? 'true' : 'false',
'deprecation' => $deprecation,
]
);
return $template->render();
}
public function getReturnType(): Type
{
return $this->returnType;
}
private function getTemplate(string $template): \Text_Template
{
$filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template;
if (!isset(self::$templates[$filename])) {
self::$templates[$filename] = new \Text_Template($filename);
}
return self::$templates[$filename];
}
/**
* Returns the parameters of a function or method.
*
* @throws RuntimeException
*/
private static function getMethodParameters(\ReflectionMethod $method, bool $forCall = false): string
{
$parameters = [];
foreach ($method->getParameters() as $i => $parameter) {
$name = '$' . $parameter->getName();
/* Note: PHP extensions may use empty names for reference arguments
* or "..." for methods taking a variable number of arguments.
*/
if ($name === '$' || $name === '$...') {
$name = '$arg' . $i;
}
if ($parameter->isVariadic()) {
if ($forCall) {
continue;
}
$name = '...' . $name;
}
$nullable = '';
$default = '';
$reference = '';
$typeDeclaration = '';
if (!$forCall) {
if ($parameter->hasType() && $parameter->allowsNull()) {
$nullable = '?';
}
if ($parameter->hasType()) {
$type = $parameter->getType();
if ($type instanceof \ReflectionNamedType && $type->getName() !== 'self') {
$typeDeclaration = $type->getName() . ' ';
} else {
try {
$class = $parameter->getClass();
} catch (\ReflectionException $e) {
throw new RuntimeException(
\sprintf(
'Cannot mock %s::%s() because a class or ' .
'interface used in the signature is not loaded',
$method->getDeclaringClass()->getName(),
$method->getName()
),
0,
$e
);
}
if ($class !== null) {
$typeDeclaration = $class->getName() . ' ';
}
}
}
if (!$parameter->isVariadic()) {
if ($parameter->isDefaultValueAvailable()) {
try {
$value = \var_export($parameter->getDefaultValue(), true);
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$default = ' = ' . $value;
} elseif ($parameter->isOptional()) {
$default = ' = null';
}
}
}
if ($parameter->isPassedByReference()) {
$reference = '&';
}
$parameters[] = $nullable . $typeDeclaration . $reference . $name . $default;
}
return \implode(', ', $parameters);
}
private static function deriveReturnType(\ReflectionMethod $method): Type
{
$returnType = $method->getReturnType();
if ($returnType === null) {
return new UnknownType();
}
// @see https://bugs.php.net/bug.php?id=70722
if ($returnType->getName() === 'self') {
return ObjectType::fromName($method->getDeclaringClass()->getName(), $returnType->allowsNull());
}
// @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/406
if ($returnType->getName() === 'parent') {
$parentClass = $method->getDeclaringClass()->getParentClass();
if ($parentClass === false) {
throw new RuntimeException(
\sprintf(
'Cannot mock %s::%s because "parent" return type declaration is used but %s does not have a parent class',
$method->getDeclaringClass()->getName(),
$method->getName(),
$method->getDeclaringClass()->getName()
)
);
}
return ObjectType::fromName($parentClass->getName(), $returnType->allowsNull());
}
return Type::fromName($returnType->getName(), $returnType->allowsNull());
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface SelfDescribing
{
/**
* Returns a string representation of the object.
*/
public function toString(): string;
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface IncompleteTest
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use DeepCopy\DeepCopy;
use PHPUnit\Framework\Constraint\Exception as ExceptionConstraint;
use PHPUnit\Framework\Constraint\ExceptionCode;
use PHPUnit\Framework\Constraint\ExceptionMessage;
use PHPUnit\Framework\Constraint\ExceptionMessageRegularExpression;
use PHPUnit\Framework\Error\Deprecated;
use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning as WarningError;
use PHPUnit\Framework\MockObject\Generator as MockGenerator;
use PHPUnit\Framework\MockObject\MockBuilder;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount as AnyInvokedCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtIndex as InvokedAtIndexMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount as InvokedAtLeastCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub;
use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub;
use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub;
use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub;
use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub;
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub;
use PHPUnit\Runner\BaseTestRunner;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\Util\Exception as UtilException;
use PHPUnit\Util\GlobalState;
use PHPUnit\Util\PHP\AbstractPhpProcess;
use PHPUnit\Util\Test as TestUtil;
use PHPUnit\Util\Type;
use Prophecy\Exception\Prediction\PredictionException;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophet;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Comparator\Factory as ComparatorFactory;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Exporter\Exporter;
use SebastianBergmann\GlobalState\Blacklist;
use SebastianBergmann\GlobalState\Restorer;
use SebastianBergmann\GlobalState\Snapshot;
use SebastianBergmann\ObjectEnumerator\Enumerator;
abstract class TestCase extends Assert implements SelfDescribing, Test
{
private const LOCALE_CATEGORIES = [\LC_ALL, \LC_COLLATE, \LC_CTYPE, \LC_MONETARY, \LC_NUMERIC, \LC_TIME];
/**
* @var bool
*/
protected $backupGlobals;
/**
* @var string[]
*/
protected $backupGlobalsBlacklist = [];
/**
* @var bool
*/
protected $backupStaticAttributes;
/**
* @var array<string,array<int,string>>
*/
protected $backupStaticAttributesBlacklist = [];
/**
* @var bool
*/
protected $runTestInSeparateProcess;
/**
* @var bool
*/
protected $preserveGlobalState = true;
/**
* @var bool
*/
private $runClassInSeparateProcess;
/**
* @var bool
*/
private $inIsolation = false;
/**
* @var array
*/
private $data;
/**
* @var string
*/
private $dataName;
/**
* @var null|string
*/
private $expectedException;
/**
* @var null|string
*/
private $expectedExceptionMessage;
/**
* @var null|string
*/
private $expectedExceptionMessageRegExp;
/**
* @var null|int|string
*/
private $expectedExceptionCode;
/**
* @var string
*/
private $name = '';
/**
* @var string[]
*/
private $dependencies = [];
/**
* @var array
*/
private $dependencyInput = [];
/**
* @var array<string,string>
*/
private $iniSettings = [];
/**
* @var array
*/
private $locale = [];
/**
* @var MockObject[]
*/
private $mockObjects = [];
/**
* @var MockGenerator
*/
private $mockObjectGenerator;
/**
* @var int
*/
private $status = BaseTestRunner::STATUS_UNKNOWN;
/**
* @var string
*/
private $statusMessage = '';
/**
* @var int
*/
private $numAssertions = 0;
/**
* @var TestResult
*/
private $result;
/**
* @var mixed
*/
private $testResult;
/**
* @var string
*/
private $output = '';
/**
* @var string
*/
private $outputExpectedRegex;
/**
* @var string
*/
private $outputExpectedString;
/**
* @var mixed
*/
private $outputCallback = false;
/**
* @var bool
*/
private $outputBufferingActive = false;
/**
* @var int
*/
private $outputBufferingLevel;
/**
* @var bool
*/
private $outputRetrievedForAssertion = false;
/**
* @var Snapshot
*/
private $snapshot;
/**
* @var \Prophecy\Prophet
*/
private $prophet;
/**
* @var bool
*/
private $beStrictAboutChangesToGlobalState = false;
/**
* @var bool
*/
private $registerMockObjectsFromTestArgumentsRecursively = false;
/**
* @var string[]
*/
private $warnings = [];
/**
* @var string[]
*/
private $groups = [];
/**
* @var bool
*/
private $doesNotPerformAssertions = false;
/**
* @var Comparator[]
*/
private $customComparators = [];
/**
* @var string[]
*/
private $doubledTypes = [];
/**
* Returns a matcher that matches when the method is executed
* zero or more times.
*/
public static function any(): AnyInvokedCountMatcher
{
return new AnyInvokedCountMatcher;
}
/**
* Returns a matcher that matches when the method is never executed.
*/
public static function never(): InvokedCountMatcher
{
return new InvokedCountMatcher(0);
}
/**
* Returns a matcher that matches when the method is executed
* at least N times.
*/
public static function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher
{
return new InvokedAtLeastCountMatcher(
$requiredInvocations
);
}
/**
* Returns a matcher that matches when the method is executed at least once.
*/
public static function atLeastOnce(): InvokedAtLeastOnceMatcher
{
return new InvokedAtLeastOnceMatcher;
}
/**
* Returns a matcher that matches when the method is executed exactly once.
*/
public static function once(): InvokedCountMatcher
{
return new InvokedCountMatcher(1);
}
/**
* Returns a matcher that matches when the method is executed
* exactly $count times.
*/
public static function exactly(int $count): InvokedCountMatcher
{
return new InvokedCountMatcher($count);
}
/**
* Returns a matcher that matches when the method is executed
* at most N times.
*/
public static function atMost(int $allowedInvocations): InvokedAtMostCountMatcher
{
return new InvokedAtMostCountMatcher($allowedInvocations);
}
/**
* Returns a matcher that matches when the method is executed
* at the given index.
*/
public static function at(int $index): InvokedAtIndexMatcher
{
return new InvokedAtIndexMatcher($index);
}
public static function returnValue($value): ReturnStub
{
return new ReturnStub($value);
}
public static function returnValueMap(array $valueMap): ReturnValueMapStub
{
return new ReturnValueMapStub($valueMap);
}
public static function returnArgument(int $argumentIndex): ReturnArgumentStub
{
return new ReturnArgumentStub($argumentIndex);
}
public static function returnCallback($callback): ReturnCallbackStub
{
return new ReturnCallbackStub($callback);
}
/**
* Returns the current object.
*
* This method is useful when mocking a fluent interface.
*/
public static function returnSelf(): ReturnSelfStub
{
return new ReturnSelfStub;
}
public static function throwException(\Throwable $exception): ExceptionStub
{
return new ExceptionStub($exception);
}
public static function onConsecutiveCalls(...$args): ConsecutiveCallsStub
{
return new ConsecutiveCallsStub($args);
}
/**
* @param string $name
* @param string $dataName
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function __construct($name = null, array $data = [], $dataName = '')
{
if ($name !== null) {
$this->setName($name);
}
$this->data = $data;
$this->dataName = $dataName;
}
/**
* This method is called before the first test of this test class is run.
*/
public static function setUpBeforeClass(): void
{
}
/**
* This method is called after the last test of this test class is run.
*/
public static function tearDownAfterClass(): void
{
}
/**
* This method is called before each test.
*/
protected function setUp(): void
{
}
/**
* This method is called after each test.
*/
protected function tearDown(): void
{
}
/**
* Returns a string representation of the test case.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*/
public function toString(): string
{
try {
$class = new \ReflectionClass($this);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
$buffer = \sprintf(
'%s::%s',
$class->name,
$this->getName(false)
);
return $buffer . $this->getDataSetAsString();
}
public function count(): int
{
return 1;
}
public function getActualOutputForAssertion(): string
{
$this->outputRetrievedForAssertion = true;
return $this->getActualOutput();
}
public function expectOutputRegex(string $expectedRegex): void
{
$this->outputExpectedRegex = $expectedRegex;
}
public function expectOutputString(string $expectedString): void
{
$this->outputExpectedString = $expectedString;
}
/**
* @psalm-param class-string<\Throwable> $exception
*/
public function expectException(string $exception): void
{
$this->expectedException = $exception;
}
/**
* @param int|string $code
*/
public function expectExceptionCode($code): void
{
$this->expectedExceptionCode = $code;
}
public function expectExceptionMessage(string $message): void
{
$this->expectedExceptionMessage = $message;
}
public function expectExceptionMessageMatches(string $regularExpression): void
{
$this->expectedExceptionMessageRegExp = $regularExpression;
}
/**
* @deprecated Use expectExceptionMessageMatches() instead
*/
public function expectExceptionMessageRegExp(string $regularExpression): void
{
$this->expectExceptionMessageMatches($regularExpression);
}
/**
* Sets up an expectation for an exception to be raised by the code under test.
* Information for expected exception class, expected exception message, and
* expected exception code are retrieved from a given Exception object.
*/
public function expectExceptionObject(\Exception $exception): void
{
$this->expectException(\get_class($exception));
$this->expectExceptionMessage($exception->getMessage());
$this->expectExceptionCode($exception->getCode());
}
public function expectNotToPerformAssertions(): void
{
$this->doesNotPerformAssertions = true;
}
public function expectDeprecation(): void
{
$this->expectException(Deprecated::class);
}
public function expectDeprecationMessage(string $message): void
{
$this->expectExceptionMessage($message);
}
public function expectDeprecationMessageMatches(string $regularExpression): void
{
$this->expectExceptionMessageRegExp($regularExpression);
}
public function expectNotice(): void
{
$this->expectException(Notice::class);
}
public function expectNoticeMessage(string $message): void
{
$this->expectExceptionMessage($message);
}
public function expectNoticeMessageMatches(string $regularExpression): void
{
$this->expectExceptionMessageRegExp($regularExpression);
}
public function expectWarning(): void
{
$this->expectException(WarningError::class);
}
public function expectWarningMessage(string $message): void
{
$this->expectExceptionMessage($message);
}
public function expectWarningMessageMatches(string $regularExpression): void
{
$this->expectExceptionMessageRegExp($regularExpression);
}
public function expectError(): void
{
$this->expectException(Error::class);
}
public function expectErrorMessage(string $message): void
{
$this->expectExceptionMessage($message);
}
public function expectErrorMessageMatches(string $regularExpression): void
{
$this->expectExceptionMessageRegExp($regularExpression);
}
public function getStatus(): int
{
return $this->status;
}
public function markAsRisky(): void
{
$this->status = BaseTestRunner::STATUS_RISKY;
}
public function getStatusMessage(): string
{
return $this->statusMessage;
}
public function hasFailed(): bool
{
$status = $this->getStatus();
return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR;
}
/**
* Runs the test case and collects the results in a TestResult object.
* If no TestResult object is passed a new one will be created.
*
* @throws CodeCoverageException
* @throws UtilException
* @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException
* @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException
* @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException
* @throws \SebastianBergmann\CodeCoverage\RuntimeException
* @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function run(TestResult $result = null): TestResult
{
if ($result === null) {
$result = $this->createResult();
}
if (!$this instanceof WarningTestCase) {
$this->setTestResultObject($result);
}
if (!$this instanceof WarningTestCase &&
!$this instanceof SkippedTestCase &&
!$this->handleDependencies()) {
return $result;
}
if ($this->runInSeparateProcess()) {
$runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess;
try {
$class = new \ReflectionClass($this);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($runEntireClass) {
$template = new \Text_Template(
__DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl'
);
} else {
$template = new \Text_Template(
__DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'
);
}
if ($this->preserveGlobalState) {
$constants = GlobalState::getConstantsAsString();
$globals = GlobalState::getGlobalsAsString();
$includedFiles = GlobalState::getIncludedFilesAsString();
$iniSettings = GlobalState::getIniSettingsAsString();
} else {
$constants = '';
if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
$globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n";
} else {
$globals = '';
}
$includedFiles = '';
$iniSettings = '';
}
$coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false';
$isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false';
$isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false';
$enforcesTimeLimit = $result->enforcesTimeLimit() ? 'true' : 'false';
$isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false';
$isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false';
if (\defined('PHPUNIT_COMPOSER_INSTALL')) {
$composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, true);
} else {
$composerAutoload = '\'\'';
}
if (\defined('__PHPUNIT_PHAR__')) {
$phar = \var_export(__PHPUNIT_PHAR__, true);
} else {
$phar = '\'\'';
}
if ($result->getCodeCoverage()) {
$codeCoverageFilter = $result->getCodeCoverage()->filter();
} else {
$codeCoverageFilter = null;
}
$data = \var_export(\serialize($this->data), true);
$dataName = \var_export($this->dataName, true);
$dependencyInput = \var_export(\serialize($this->dependencyInput), true);
$includePath = \var_export(\get_include_path(), true);
$codeCoverageFilter = \var_export(\serialize($codeCoverageFilter), true);
// must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC
// the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences
$data = "'." . $data . ".'";
$dataName = "'.(" . $dataName . ").'";
$dependencyInput = "'." . $dependencyInput . ".'";
$includePath = "'." . $includePath . ".'";
$codeCoverageFilter = "'." . $codeCoverageFilter . ".'";
$configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? '';
$var = [
'composerAutoload' => $composerAutoload,
'phar' => $phar,
'filename' => $class->getFileName(),
'className' => $class->getName(),
'collectCodeCoverageInformation' => $coverage,
'data' => $data,
'dataName' => $dataName,
'dependencyInput' => $dependencyInput,
'constants' => $constants,
'globals' => $globals,
'include_path' => $includePath,
'included_files' => $includedFiles,
'iniSettings' => $iniSettings,
'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything,
'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests,
'enforcesTimeLimit' => $enforcesTimeLimit,
'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests,
'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests,
'codeCoverageFilter' => $codeCoverageFilter,
'configurationFilePath' => $configurationFilePath,
'name' => $this->getName(false),
];
if (!$runEntireClass) {
$var['methodName'] = $this->name;
}
$template->setVar($var);
$php = AbstractPhpProcess::factory();
$php->runTestJob($template->render(), $this, $result);
} else {
$result->run($this);
}
$this->result = null;
return $result;
}
/**
* Returns a builder object to create mock objects using a fluent interface.
*
* @param string|string[] $className
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType>|string[] $className
* @psalm-return MockBuilder<RealInstanceType>
*/
public function getMockBuilder($className): MockBuilder
{
$this->recordDoubledType($className);
return new MockBuilder($this, $className);
}
public function registerComparator(Comparator $comparator): void
{
ComparatorFactory::getInstance()->register($comparator);
$this->customComparators[] = $comparator;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*
* @deprecated Invoking this method has no effect; it will be removed in PHPUnit 9
*/
public function setUseErrorHandler(bool $useErrorHandler): void
{
}
/**
* @return string[]
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function doubledTypes(): array
{
return \array_unique($this->doubledTypes);
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getGroups(): array
{
return $this->groups;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setGroups(array $groups): void
{
$this->groups = $groups;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getAnnotations(): array
{
return TestUtil::parseTestMethodAnnotations(
\get_class($this),
$this->name
);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getName(bool $withDataSet = true): string
{
if ($withDataSet) {
return $this->name . $this->getDataSetAsString(false);
}
return $this->name;
}
/**
* Returns the size of the test.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getSize(): int
{
return TestUtil::getSize(
\get_class($this),
$this->getName(false)
);
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function hasSize(): bool
{
return $this->getSize() !== TestUtil::UNKNOWN;
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function isSmall(): bool
{
return $this->getSize() === TestUtil::SMALL;
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function isMedium(): bool
{
return $this->getSize() === TestUtil::MEDIUM;
}
/**
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function isLarge(): bool
{
return $this->getSize() === TestUtil::LARGE;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getActualOutput(): string
{
if (!$this->outputBufferingActive) {
return $this->output;
}
return (string) \ob_get_contents();
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function hasOutput(): bool
{
if ($this->output === '') {
return false;
}
if ($this->hasExpectationOnOutput()) {
return false;
}
return true;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function doesNotPerformAssertions(): bool
{
return $this->doesNotPerformAssertions;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function hasExpectationOnOutput(): bool
{
return \is_string($this->outputExpectedString) || \is_string($this->outputExpectedRegex) || $this->outputRetrievedForAssertion;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getExpectedException(): ?string
{
return $this->expectedException;
}
/**
* @return null|int|string
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getExpectedExceptionCode()
{
return $this->expectedExceptionCode;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getExpectedExceptionMessage(): ?string
{
return $this->expectedExceptionMessage;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getExpectedExceptionMessageRegExp(): ?string
{
return $this->expectedExceptionMessageRegExp;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void
{
$this->registerMockObjectsFromTestArgumentsRecursively = $flag;
}
/**
* @throws \Throwable
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function runBare(): void
{
$this->numAssertions = 0;
$this->snapshotGlobalState();
$this->startOutputBuffering();
\clearstatcache();
$currentWorkingDirectory = \getcwd();
$hookMethods = TestUtil::getHookMethods(\get_class($this));
$hasMetRequirements = false;
try {
$this->checkRequirements();
$hasMetRequirements = true;
if ($this->inIsolation) {
foreach ($hookMethods['beforeClass'] as $method) {
$this->$method();
}
}
$this->setExpectedExceptionFromAnnotation();
$this->setDoesNotPerformAssertionsFromAnnotation();
foreach ($hookMethods['before'] as $method) {
$this->$method();
}
$this->assertPreConditions();
$this->testResult = $this->runTest();
$this->verifyMockObjects();
$this->assertPostConditions();
if (!empty($this->warnings)) {
throw new Warning(
\implode(
"\n",
\array_unique($this->warnings)
)
);
}
$this->status = BaseTestRunner::STATUS_PASSED;
} catch (IncompleteTest $e) {
$this->status = BaseTestRunner::STATUS_INCOMPLETE;
$this->statusMessage = $e->getMessage();
} catch (SkippedTest $e) {
$this->status = BaseTestRunner::STATUS_SKIPPED;
$this->statusMessage = $e->getMessage();
} catch (Warning $e) {
$this->status = BaseTestRunner::STATUS_WARNING;
$this->statusMessage = $e->getMessage();
} catch (AssertionFailedError $e) {
$this->status = BaseTestRunner::STATUS_FAILURE;
$this->statusMessage = $e->getMessage();
} catch (PredictionException $e) {
$this->status = BaseTestRunner::STATUS_FAILURE;
$this->statusMessage = $e->getMessage();
} catch (\Throwable $_e) {
$e = $_e;
$this->status = BaseTestRunner::STATUS_ERROR;
$this->statusMessage = $_e->getMessage();
}
$this->mockObjects = [];
$this->prophet = null;
// Tear down the fixture. An exception raised in tearDown() will be
// caught and passed on when no exception was raised before.
try {
if ($hasMetRequirements) {
foreach ($hookMethods['after'] as $method) {
$this->$method();
}
if ($this->inIsolation) {
foreach ($hookMethods['afterClass'] as $method) {
$this->$method();
}
}
}
} catch (\Throwable $_e) {
$e = $e ?? $_e;
}
try {
$this->stopOutputBuffering();
} catch (RiskyTestError $_e) {
$e = $e ?? $_e;
}
if (isset($_e)) {
$this->status = BaseTestRunner::STATUS_ERROR;
$this->statusMessage = $_e->getMessage();
}
\clearstatcache();
if ($currentWorkingDirectory !== \getcwd()) {
\chdir($currentWorkingDirectory);
}
$this->restoreGlobalState();
$this->unregisterCustomComparators();
$this->cleanupIniSettings();
$this->cleanupLocaleSettings();
\libxml_clear_errors();
// Perform assertion on output.
if (!isset($e)) {
try {
if ($this->outputExpectedRegex !== null) {
$this->assertRegExp($this->outputExpectedRegex, $this->output);
} elseif ($this->outputExpectedString !== null) {
$this->assertEquals($this->outputExpectedString, $this->output);
}
} catch (\Throwable $_e) {
$e = $_e;
}
}
// Workaround for missing "finally".
if (isset($e)) {
if ($e instanceof PredictionException) {
$e = new AssertionFailedError($e->getMessage());
}
$this->onNotSuccessfulTest($e);
}
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setName(string $name): void
{
$this->name = $name;
}
/**
* @param string[] $dependencies
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setDependencies(array $dependencies): void
{
$this->dependencies = $dependencies;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getDependencies(): array
{
return $this->dependencies;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function hasDependencies(): bool
{
return \count($this->dependencies) > 0;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setDependencyInput(array $dependencyInput): void
{
$this->dependencyInput = $dependencyInput;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getDependencyInput(): array
{
return $this->dependencyInput;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState): void
{
$this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setBackupGlobals(?bool $backupGlobals): void
{
if ($this->backupGlobals === null && $backupGlobals !== null) {
$this->backupGlobals = $backupGlobals;
}
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setBackupStaticAttributes(?bool $backupStaticAttributes): void
{
if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) {
$this->backupStaticAttributes = $backupStaticAttributes;
}
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void
{
if ($this->runTestInSeparateProcess === null) {
$this->runTestInSeparateProcess = $runTestInSeparateProcess;
}
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess): void
{
if ($this->runClassInSeparateProcess === null) {
$this->runClassInSeparateProcess = $runClassInSeparateProcess;
}
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setPreserveGlobalState(bool $preserveGlobalState): void
{
$this->preserveGlobalState = $preserveGlobalState;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setInIsolation(bool $inIsolation): void
{
$this->inIsolation = $inIsolation;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function isInIsolation(): bool
{
return $this->inIsolation;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getResult()
{
return $this->testResult;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setResult($result): void
{
$this->testResult = $result;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setOutputCallback(callable $callback): void
{
$this->outputCallback = $callback;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getTestResultObject(): ?TestResult
{
return $this->result;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function setTestResultObject(TestResult $result): void
{
$this->result = $result;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function registerMockObject(MockObject $mockObject): void
{
$this->mockObjects[] = $mockObject;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function addToAssertionCount(int $count): void
{
$this->numAssertions += $count;
}
/**
* Returns the number of assertions performed by this test.
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getNumAssertions(): int
{
return $this->numAssertions;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function usesDataProvider(): bool
{
return !empty($this->data);
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function dataDescription(): string
{
return \is_string($this->dataName) ? $this->dataName : '';
}
/**
* @return int|string
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function dataName()
{
return $this->dataName;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getDataSetAsString(bool $includeData = true): string
{
$buffer = '';
if (!empty($this->data)) {
if (\is_int($this->dataName)) {
$buffer .= \sprintf(' with data set #%d', $this->dataName);
} else {
$buffer .= \sprintf(' with data set "%s"', $this->dataName);
}
$exporter = new Exporter;
if ($includeData) {
$buffer .= \sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data));
}
}
return $buffer;
}
/**
* Gets the data set of a TestCase.
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function getProvidedData(): array
{
return $this->data;
}
/**
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function addWarning(string $warning): void
{
$this->warnings[] = $warning;
}
/**
* Override to run the test and assert its state.
*
* @throws AssertionFailedError
* @throws Exception
* @throws ExpectationFailedException
* @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
* @throws \Throwable
*/
protected function runTest()
{
if (\trim($this->name) === '') {
throw new Exception(
'PHPUnit\Framework\TestCase::$name must be a non-blank string.'
);
}
$testArguments = \array_merge($this->data, $this->dependencyInput);
$this->registerMockObjectsFromTestArguments($testArguments);
try {
$testResult = $this->{$this->name}(...\array_values($testArguments));
} catch (\Throwable $exception) {
if (!$this->checkExceptionExpectations($exception)) {
throw $exception;
}
if ($this->expectedException !== null) {
$this->assertThat(
$exception,
new ExceptionConstraint(
$this->expectedException
)
);
}
if ($this->expectedExceptionMessage !== null) {
$this->assertThat(
$exception,
new ExceptionMessage(
$this->expectedExceptionMessage
)
);
}
if ($this->expectedExceptionMessageRegExp !== null) {
$this->assertThat(
$exception,
new ExceptionMessageRegularExpression(
$this->expectedExceptionMessageRegExp
)
);
}
if ($this->expectedExceptionCode !== null) {
$this->assertThat(
$exception,
new ExceptionCode(
$this->expectedExceptionCode
)
);
}
return;
}
if ($this->expectedException !== null) {
$this->assertThat(
null,
new ExceptionConstraint(
$this->expectedException
)
);
} elseif ($this->expectedExceptionMessage !== null) {
$this->numAssertions++;
throw new AssertionFailedError(
\sprintf(
'Failed asserting that exception with message "%s" is thrown',
$this->expectedExceptionMessage
)
);
} elseif ($this->expectedExceptionMessageRegExp !== null) {
$this->numAssertions++;
throw new AssertionFailedError(
\sprintf(
'Failed asserting that exception with message matching "%s" is thrown',
$this->expectedExceptionMessageRegExp
)
);
} elseif ($this->expectedExceptionCode !== null) {
$this->numAssertions++;
throw new AssertionFailedError(
\sprintf(
'Failed asserting that exception with code "%s" is thrown',
$this->expectedExceptionCode
)
);
}
return $testResult;
}
/**
* This method is a wrapper for the ini_set() function that automatically
* resets the modified php.ini setting to its original value after the
* test is run.
*
* @throws Exception
*/
protected function iniSet(string $varName, string $newValue): void
{
$currentValue = \ini_set($varName, $newValue);
if ($currentValue !== false) {
$this->iniSettings[$varName] = $currentValue;
} else {
throw new Exception(
\sprintf(
'INI setting "%s" could not be set to "%s".',
$varName,
$newValue
)
);
}
}
/**
* This method is a wrapper for the setlocale() function that automatically
* resets the locale to its original value after the test is run.
*
* @throws Exception
*/
protected function setLocale(...$args): void
{
if (\count($args) < 2) {
throw new Exception;
}
[$category, $locale] = $args;
if (\defined('LC_MESSAGES')) {
$categories[] = \LC_MESSAGES;
}
if (!\in_array($category, self::LOCALE_CATEGORIES, true)) {
throw new Exception;
}
if (!\is_array($locale) && !\is_string($locale)) {
throw new Exception;
}
$this->locale[$category] = \setlocale($category, 0);
$result = \setlocale(...$args);
if ($result === false) {
throw new Exception(
'The locale functionality is not implemented on your platform, ' .
'the specified locale does not exist or the category name is ' .
'invalid.'
);
}
}
/**
* Makes configurable stub for the specified class.
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType> $originalClassName
* @psalm-return Stub&RealInstanceType
*/
protected function createStub(string $originalClassName): Stub
{
return $this->createMock($originalClassName);
}
/**
* Returns a mock object for the specified class.
*
* @param string|string[] $originalClassName
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType>|string[] $originalClassName
* @psalm-return MockObject&RealInstanceType
*/
protected function createMock($originalClassName): MockObject
{
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->getMock();
}
/**
* Returns a configured mock object for the specified class.
*
* @param string|string[] $originalClassName
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType>|string[] $originalClassName
* @psalm-return MockObject&RealInstanceType
*/
protected function createConfiguredMock($originalClassName, array $configuration): MockObject
{
$o = $this->createMock($originalClassName);
foreach ($configuration as $method => $return) {
$o->method($method)->willReturn($return);
}
return $o;
}
/**
* Returns a partial mock object for the specified class.
*
* @param string|string[] $originalClassName
* @param string[] $methods
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType>|string[] $originalClassName
* @psalm-return MockObject&RealInstanceType
*/
protected function createPartialMock($originalClassName, array $methods): MockObject
{
$class_names = \is_array($originalClassName) ? $originalClassName : [$originalClassName];
foreach ($class_names as $class_name) {
$reflection = new \ReflectionClass($class_name);
$mockedMethodsThatDontExist = \array_filter(
$methods,
static function (string $method) use ($reflection) {
return !$reflection->hasMethod($method);
}
);
if ($mockedMethodsThatDontExist) {
$this->addWarning(
\sprintf(
'createPartialMock called with method(s) %s that do not exist in %s. This will not be allowed in future versions of PHPUnit.',
\implode(', ', $mockedMethodsThatDontExist),
$class_name
)
);
}
}
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->setMethods(empty($methods) ? null : $methods)
->getMock();
}
/**
* Returns a test proxy for the specified class.
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType> $originalClassName
* @psalm-return MockObject&RealInstanceType
*/
protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject
{
return $this->getMockBuilder($originalClassName)
->setConstructorArgs($constructorArguments)
->enableProxyingToOriginalMethods()
->getMock();
}
/**
* Mocks the specified class and returns the name of the mocked class.
*
* @param string $originalClassName
* @param array $methods
* @param string $mockClassName
* @param bool $callOriginalConstructor
* @param bool $callOriginalClone
* @param bool $callAutoload
* @param bool $cloneArguments
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType>|string $originalClassName
* @psalm-return class-string<MockObject&RealInstanceType>
*/
protected function getMockClass($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false): string
{
$this->recordDoubledType($originalClassName);
$mock = $this->getMockObjectGenerator()->getMock(
$originalClassName,
$methods,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$cloneArguments
);
return \get_class($mock);
}
/**
* Returns a mock object for the specified abstract class with all abstract
* methods of the class mocked. Concrete methods are not mocked by default.
* To mock concrete methods, use the 7th parameter ($mockedMethods).
*
* @param string $originalClassName
* @param string $mockClassName
* @param bool $callOriginalConstructor
* @param bool $callOriginalClone
* @param bool $callAutoload
* @param array $mockedMethods
* @param bool $cloneArguments
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType> $originalClassName
* @psalm-return MockObject&RealInstanceType
*/
protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject
{
$this->recordDoubledType($originalClassName);
$mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass(
$originalClassName,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$mockedMethods,
$cloneArguments
);
$this->registerMockObject($mockObject);
return $mockObject;
}
/**
* Returns a mock object based on the given WSDL file.
*
* @param string $wsdlFile
* @param string $originalClassName
* @param string $mockClassName
* @param bool $callOriginalConstructor
* @param array $options An array of options passed to SOAPClient::_construct
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType>|string $originalClassName
* @psalm-return MockObject&RealInstanceType
*/
protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = []): MockObject
{
$this->recordDoubledType('SoapClient');
if ($originalClassName === '') {
$fileName = \pathinfo(\basename(\parse_url($wsdlFile)['path']), \PATHINFO_FILENAME);
$originalClassName = \preg_replace('/[^a-zA-Z0-9_]/', '', $fileName);
}
if (!\class_exists($originalClassName)) {
eval(
$this->getMockObjectGenerator()->generateClassFromWsdl(
$wsdlFile,
$originalClassName,
$methods,
$options
)
);
}
$mockObject = $this->getMockObjectGenerator()->getMock(
$originalClassName,
$methods,
['', $options],
$mockClassName,
$callOriginalConstructor,
false,
false
);
$this->registerMockObject($mockObject);
return $mockObject;
}
/**
* Returns a mock object for the specified trait with all abstract methods
* of the trait mocked. Concrete methods to mock can be specified with the
* `$mockedMethods` parameter.
*
* @param string $traitName
* @param string $mockClassName
* @param bool $callOriginalConstructor
* @param bool $callOriginalClone
* @param bool $callAutoload
* @param array $mockedMethods
* @param bool $cloneArguments
*/
protected function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject
{
$this->recordDoubledType($traitName);
$mockObject = $this->getMockObjectGenerator()->getMockForTrait(
$traitName,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload,
$mockedMethods,
$cloneArguments
);
$this->registerMockObject($mockObject);
return $mockObject;
}
/**
* Returns an object for the specified trait.
*
* @param string $traitName
* @param string $traitClassName
* @param bool $callOriginalConstructor
* @param bool $callOriginalClone
* @param bool $callAutoload
*
* @return object
*/
protected function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)/*: object*/
{
$this->recordDoubledType($traitName);
return $this->getMockObjectGenerator()->getObjectForTrait(
$traitName,
$traitClassName,
$callAutoload,
$callOriginalConstructor,
$arguments
);
}
/**
* @param null|string $classOrInterface
*
* @throws \Prophecy\Exception\Doubler\ClassNotFoundException
* @throws \Prophecy\Exception\Doubler\DoubleException
* @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException
*
* @psalm-template RealInstanceType of object
* @psalm-param class-string<RealInstanceType>|null $classOrInterface
* @psalm-return ObjectProphecy<RealInstanceType>
*/
protected function prophesize($classOrInterface = null): ObjectProphecy
{
if (\is_string($classOrInterface)) {
$this->recordDoubledType($classOrInterface);
}
return $this->getProphet()->prophesize($classOrInterface);
}
/**
* Creates a default TestResult object.
*
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
protected function createResult(): TestResult
{
return new TestResult;
}
/**
* Performs assertions shared by all tests of a test case.
*
* This method is called between setUp() and test.
*/
protected function assertPreConditions(): void
{
}
/**
* Performs assertions shared by all tests of a test case.
*
* This method is called between test and tearDown().
*/
protected function assertPostConditions(): void
{
}
/**
* This method is called when a test method did not execute successfully.
*
* @throws \Throwable
*/
protected function onNotSuccessfulTest(\Throwable $t): void
{
throw $t;
}
private function setExpectedExceptionFromAnnotation(): void
{
if ($this->name === null) {
return;
}
try {
$expectedException = TestUtil::getExpectedException(
\get_class($this),
$this->name
);
if ($expectedException !== false) {
$this->addWarning('The @expectedException, @expectedExceptionCode, @expectedExceptionMessage, and @expectedExceptionMessageRegExp annotations are deprecated. They will be removed in PHPUnit 9. Refactor your test to use expectException(), expectExceptionCode(), expectExceptionMessage(), or expectExceptionMessageRegExp() instead.');
$this->expectException($expectedException['class']);
if ($expectedException['code'] !== null) {
$this->expectExceptionCode($expectedException['code']);
}
if ($expectedException['message'] !== '') {
$this->expectExceptionMessage($expectedException['message']);
} elseif ($expectedException['message_regex'] !== '') {
$this->expectExceptionMessageRegExp($expectedException['message_regex']);
}
}
} catch (UtilException $e) {
}
}
/**
* @throws Warning
* @throws SkippedTestError
* @throws SyntheticSkippedError
*/
private function checkRequirements(): void
{
if (!$this->name || !\method_exists($this, $this->name)) {
return;
}
$missingRequirements = TestUtil::getMissingRequirements(
\get_class($this),
$this->name
);
if (!empty($missingRequirements)) {
$this->markTestSkipped(\implode(\PHP_EOL, $missingRequirements));
}
}
/**
* @throws \Throwable
*/
private function verifyMockObjects(): void
{
foreach ($this->mockObjects as $mockObject) {
if ($mockObject->__phpunit_hasMatchers()) {
$this->numAssertions++;
}
$mockObject->__phpunit_verify(
$this->shouldInvocationMockerBeReset($mockObject)
);
}
if ($this->prophet !== null) {
try {
$this->prophet->checkPredictions();
} finally {
foreach ($this->prophet->getProphecies() as $objectProphecy) {
foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) {
foreach ($methodProphecies as $methodProphecy) {
\assert($methodProphecy instanceof MethodProphecy);
$this->numAssertions += \count($methodProphecy->getCheckedPredictions());
}
}
}
}
}
}
private function handleDependencies(): bool
{
if (!empty($this->dependencies) && !$this->inIsolation) {
$className = \get_class($this);
$passed = $this->result->passed();
$passedKeys = \array_keys($passed);
foreach ($passedKeys as $key => $value) {
$pos = \strpos($value, ' with data set');
if ($pos !== false) {
$passedKeys[$key] = \substr($value, 0, $pos);
}
}
$passedKeys = \array_flip(\array_unique($passedKeys));
foreach ($this->dependencies as $dependency) {
$deepClone = false;
$shallowClone = false;
if (empty($dependency)) {
$this->markSkippedForNotSpecifyingDependency();
return false;
}
if (\strpos($dependency, 'clone ') === 0) {
$deepClone = true;
$dependency = \substr($dependency, \strlen('clone '));
} elseif (\strpos($dependency, '!clone ') === 0) {
$deepClone = false;
$dependency = \substr($dependency, \strlen('!clone '));
}
if (\strpos($dependency, 'shallowClone ') === 0) {
$shallowClone = true;
$dependency = \substr($dependency, \strlen('shallowClone '));
} elseif (\strpos($dependency, '!shallowClone ') === 0) {
$shallowClone = false;
$dependency = \substr($dependency, \strlen('!shallowClone '));
}
if (\strpos($dependency, '::') === false) {
$dependency = $className . '::' . $dependency;
}
if (!isset($passedKeys[$dependency])) {
if (!$this->isCallableTestMethod($dependency)) {
$this->warnAboutDependencyThatDoesNotExist($dependency);
} else {
$this->markSkippedForMissingDependency($dependency);
}
return false;
}
if (isset($passed[$dependency])) {
if ($passed[$dependency]['size'] !== TestUtil::UNKNOWN &&
$this->getSize() !== TestUtil::UNKNOWN &&
$passed[$dependency]['size'] > $this->getSize()) {
$this->result->addError(
$this,
new SkippedTestError(
'This test depends on a test that is larger than itself.'
),
0
);
return false;
}
if ($deepClone) {
$deepCopy = new DeepCopy;
$deepCopy->skipUncloneable(false);
$this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']);
} elseif ($shallowClone) {
$this->dependencyInput[$dependency] = clone $passed[$dependency]['result'];
} else {
$this->dependencyInput[$dependency] = $passed[$dependency]['result'];
}
} else {
$this->dependencyInput[$dependency] = null;
}
}
}
return true;
}
private function markSkippedForNotSpecifyingDependency(): void
{
$this->status = BaseTestRunner::STATUS_SKIPPED;
$this->result->startTest($this);
$this->result->addError(
$this,
new SkippedTestError(
\sprintf('This method has an invalid @depends annotation.')
),
0
);
$this->result->endTest($this, 0);
}
private function markSkippedForMissingDependency(string $dependency): void
{
$this->status = BaseTestRunner::STATUS_SKIPPED;
$this->result->startTest($this);
$this->result->addError(
$this,
new SkippedTestError(
\sprintf(
'This test depends on "%s" to pass.',
$dependency
)
),
0
);
$this->result->endTest($this, 0);
}
private function warnAboutDependencyThatDoesNotExist(string $dependency): void
{
$this->status = BaseTestRunner::STATUS_WARNING;
$this->result->startTest($this);
$this->result->addWarning(
$this,
new Warning(
\sprintf(
'This test depends on "%s" which does not exist.',
$dependency
)
),
0
);
$this->result->endTest($this, 0);
}
/**
* Get the mock object generator, creating it if it doesn't exist.
*/
private function getMockObjectGenerator(): MockGenerator
{
if ($this->mockObjectGenerator === null) {
$this->mockObjectGenerator = new MockGenerator;
}
return $this->mockObjectGenerator;
}
private function startOutputBuffering(): void
{
\ob_start();
$this->outputBufferingActive = true;
$this->outputBufferingLevel = \ob_get_level();
}
/**
* @throws RiskyTestError
*/
private function stopOutputBuffering(): void
{
if (\ob_get_level() !== $this->outputBufferingLevel) {
while (\ob_get_level() >= $this->outputBufferingLevel) {
\ob_end_clean();
}
throw new RiskyTestError(
'Test code or tested code did not (only) close its own output buffers'
);
}
$this->output = \ob_get_contents();
if ($this->outputCallback !== false) {
$this->output = (string) \call_user_func($this->outputCallback, $this->output);
}
\ob_end_clean();
$this->outputBufferingActive = false;
$this->outputBufferingLevel = \ob_get_level();
}
private function snapshotGlobalState(): void
{
if ($this->runTestInSeparateProcess || $this->inIsolation ||
(!$this->backupGlobals && !$this->backupStaticAttributes)) {
return;
}
$this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === true);
}
/**
* @throws RiskyTestError
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function restoreGlobalState(): void
{
if (!$this->snapshot instanceof Snapshot) {
return;
}
if ($this->beStrictAboutChangesToGlobalState) {
try {
$this->compareGlobalStateSnapshots(
$this->snapshot,
$this->createGlobalStateSnapshot($this->backupGlobals === true)
);
} catch (RiskyTestError $rte) {
// Intentionally left empty
}
}
$restorer = new Restorer;
if ($this->backupGlobals) {
$restorer->restoreGlobalVariables($this->snapshot);
}
if ($this->backupStaticAttributes) {
$restorer->restoreStaticAttributes($this->snapshot);
}
$this->snapshot = null;
if (isset($rte)) {
throw $rte;
}
}
private function createGlobalStateSnapshot(bool $backupGlobals): Snapshot
{
$blacklist = new Blacklist;
foreach ($this->backupGlobalsBlacklist as $globalVariable) {
$blacklist->addGlobalVariable($globalVariable);
}
if (!\defined('PHPUNIT_TESTSUITE')) {
$blacklist->addClassNamePrefix('PHPUnit');
$blacklist->addClassNamePrefix('SebastianBergmann\CodeCoverage');
$blacklist->addClassNamePrefix('SebastianBergmann\FileIterator');
$blacklist->addClassNamePrefix('SebastianBergmann\Invoker');
$blacklist->addClassNamePrefix('SebastianBergmann\Timer');
$blacklist->addClassNamePrefix('PHP_Token');
$blacklist->addClassNamePrefix('Symfony');
$blacklist->addClassNamePrefix('Text_Template');
$blacklist->addClassNamePrefix('Doctrine\Instantiator');
$blacklist->addClassNamePrefix('Prophecy');
foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) {
foreach ($attributes as $attribute) {
$blacklist->addStaticAttribute($class, $attribute);
}
}
}
return new Snapshot(
$blacklist,
$backupGlobals,
(bool) $this->backupStaticAttributes,
false,
false,
false,
false,
false,
false,
false
);
}
/**
* @throws RiskyTestError
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after): void
{
$backupGlobals = $this->backupGlobals === null || $this->backupGlobals;
if ($backupGlobals) {
$this->compareGlobalStateSnapshotPart(
$before->globalVariables(),
$after->globalVariables(),
"--- Global variables before the test\n+++ Global variables after the test\n"
);
$this->compareGlobalStateSnapshotPart(
$before->superGlobalVariables(),
$after->superGlobalVariables(),
"--- Super-global variables before the test\n+++ Super-global variables after the test\n"
);
}
if ($this->backupStaticAttributes) {
$this->compareGlobalStateSnapshotPart(
$before->staticAttributes(),
$after->staticAttributes(),
"--- Static attributes before the test\n+++ Static attributes after the test\n"
);
}
}
/**
* @throws RiskyTestError
*/
private function compareGlobalStateSnapshotPart(array $before, array $after, string $header): void
{
if ($before != $after) {
$differ = new Differ($header);
$exporter = new Exporter;
$diff = $differ->diff(
$exporter->export($before),
$exporter->export($after)
);
throw new RiskyTestError(
$diff
);
}
}
private function getProphet(): Prophet
{
if ($this->prophet === null) {
$this->prophet = new Prophet;
}
return $this->prophet;
}
/**
* @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
*/
private function shouldInvocationMockerBeReset(MockObject $mock): bool
{
$enumerator = new Enumerator;
foreach ($enumerator->enumerate($this->dependencyInput) as $object) {
if ($mock === $object) {
return false;
}
}
if (!\is_array($this->testResult) && !\is_object($this->testResult)) {
return true;
}
return !\in_array($mock, $enumerator->enumerate($this->testResult), true);
}
/**
* @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException
* @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []): void
{
if ($this->registerMockObjectsFromTestArgumentsRecursively) {
foreach ((new Enumerator)->enumerate($testArguments) as $object) {
if ($object instanceof MockObject) {
$this->registerMockObject($object);
}
}
} else {
foreach ($testArguments as $testArgument) {
if ($testArgument instanceof MockObject) {
if (Type::isCloneable($testArgument)) {
$testArgument = clone $testArgument;
}
$this->registerMockObject($testArgument);
} elseif (\is_array($testArgument) && !\in_array($testArgument, $visited, true)) {
$visited[] = $testArgument;
$this->registerMockObjectsFromTestArguments(
$testArgument,
$visited
);
}
}
}
}
private function setDoesNotPerformAssertionsFromAnnotation(): void
{
$annotations = $this->getAnnotations();
if (isset($annotations['method']['doesNotPerformAssertions'])) {
$this->doesNotPerformAssertions = true;
}
}
private function unregisterCustomComparators(): void
{
$factory = ComparatorFactory::getInstance();
foreach ($this->customComparators as $comparator) {
$factory->unregister($comparator);
}
$this->customComparators = [];
}
private function cleanupIniSettings(): void
{
foreach ($this->iniSettings as $varName => $oldValue) {
\ini_set($varName, $oldValue);
}
$this->iniSettings = [];
}
private function cleanupLocaleSettings(): void
{
foreach ($this->locale as $category => $locale) {
\setlocale($category, $locale);
}
$this->locale = [];
}
/**
* @throws Exception
*/
private function checkExceptionExpectations(\Throwable $throwable): bool
{
$result = false;
if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) {
$result = true;
}
if ($throwable instanceof Exception) {
$result = false;
}
if (\is_string($this->expectedException)) {
try {
$reflector = new \ReflectionClass($this->expectedException);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($this->expectedException === 'PHPUnit\Framework\Exception' ||
$this->expectedException === '\PHPUnit\Framework\Exception' ||
$reflector->isSubclassOf(Exception::class)) {
$result = true;
}
}
return $result;
}
private function runInSeparateProcess(): bool
{
return ($this->runTestInSeparateProcess || $this->runClassInSeparateProcess) &&
!$this->inIsolation && !$this instanceof PhptTestCase;
}
/**
* @param string|string[] $originalClassName
*/
private function recordDoubledType($originalClassName): void
{
if (\is_string($originalClassName)) {
$this->doubledTypes[] = $originalClassName;
}
if (\is_array($originalClassName)) {
foreach ($originalClassName as $_originalClassName) {
if (\is_string($_originalClassName)) {
$this->doubledTypes[] = $_originalClassName;
}
}
}
}
private function isCallableTestMethod(string $dependency): bool
{
[$className, $methodName] = \explode('::', $dependency);
if (!\class_exists($className)) {
return false;
}
try {
$class = new \ReflectionClass($className);
} catch (\ReflectionException $e) {
return false;
}
if (!$class->isSubclassOf(__CLASS__)) {
return false;
}
if (!$class->hasMethod($methodName)) {
return false;
}
try {
$method = $class->getMethod($methodName);
} catch (\ReflectionException $e) {
return false;
}
return TestUtil::isTestMethod($method);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
interface SkippedTest
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use PHPUnit\Util\Filter;
use Throwable;
/**
* Wraps Exceptions thrown by code under test.
*
* Re-instantiates Exceptions thrown by user-space code to retain their original
* class names, properties, and stack traces (but without arguments).
*
* Unlike PHPUnit\Framework_\Exception, the complete stack of previous Exceptions
* is processed.
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ExceptionWrapper extends Exception
{
/**
* @var string
*/
protected $className;
/**
* @var null|ExceptionWrapper
*/
protected $previous;
public function __construct(Throwable $t)
{
// PDOException::getCode() is a string.
// @see https://php.net/manual/en/class.pdoexception.php#95812
parent::__construct($t->getMessage(), (int) $t->getCode());
$this->setOriginalException($t);
}
public function __toString(): string
{
$string = TestFailure::exceptionToString($this);
if ($trace = Filter::getFilteredStacktrace($this)) {
$string .= "\n" . $trace;
}
if ($this->previous) {
$string .= "\nCaused by\n" . $this->previous;
}
return $string;
}
public function getClassName(): string
{
return $this->className;
}
public function getPreviousWrapped(): ?self
{
return $this->previous;
}
public function setClassName(string $className): void
{
$this->className = $className;
}
public function setOriginalException(\Throwable $t): void
{
$this->originalException($t);
$this->className = \get_class($t);
$this->file = $t->getFile();
$this->line = $t->getLine();
$this->serializableTrace = $t->getTrace();
foreach ($this->serializableTrace as $i => $call) {
unset($this->serializableTrace[$i]['args']);
}
if ($t->getPrevious()) {
$this->previous = new self($t->getPrevious());
}
}
public function getOriginalException(): ?Throwable
{
return $this->originalException();
}
/**
* Method to contain static originalException to exclude it from stacktrace to prevent the stacktrace contents,
* which can be quite big, from being garbage-collected, thus blocking memory until shutdown.
* Approach works both for var_dump() and var_export() and print_r()
*/
private function originalException(Throwable $exceptionToStore = null): ?Throwable
{
static $originalExceptions;
$instanceId = \spl_object_hash($this);
if ($exceptionToStore) {
$originalExceptions[$instanceId] = $exceptionToStore;
}
return $originalExceptions[$instanceId] ?? null;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @deprecated The `TestListener` interface is deprecated
*/
trait TestListenerDefaultImplementation
{
public function addError(Test $test, \Throwable $t, float $time): void
{
}
public function addWarning(Test $test, Warning $e, float $time): void
{
}
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
}
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
{
}
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
{
}
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
{
}
public function startTestSuite(TestSuite $suite): void
{
}
public function endTestSuite(TestSuite $suite): void
{
}
public function startTest(Test $test): void
{
}
public function endTest(Test $test, float $time): void
{
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
/**
* Logical XOR.
*/
final class LogicalXor extends Constraint
{
/**
* @var Constraint[]
*/
private $constraints = [];
public static function fromConstraints(Constraint ...$constraints): self
{
$constraint = new self;
$constraint->constraints = \array_values($constraints);
return $constraint;
}
/**
* @param Constraint[] $constraints
*/
public function setConstraints(array $constraints): void
{
$this->constraints = [];
foreach ($constraints as $constraint) {
if (!($constraint instanceof Constraint)) {
$constraint = new IsEqual(
$constraint
);
}
$this->constraints[] = $constraint;
}
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
$success = true;
$lastResult = null;
foreach ($this->constraints as $constraint) {
$result = $constraint->evaluate($other, $description, true);
if ($result === $lastResult) {
$success = false;
break;
}
$lastResult = $result;
}
if ($returnResult) {
return $success;
}
if (!$success) {
$this->fail($other, $description);
}
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
$text = '';
foreach ($this->constraints as $key => $constraint) {
if ($key > 0) {
$text .= ' xor ';
}
$text .= $constraint->toString();
}
return $text;
}
/**
* Counts the number of constraint elements.
*/
public function count(): int
{
$count = 0;
foreach ($this->constraints as $constraint) {
$count += \count($constraint);
}
return $count;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that asserts that the string it is evaluated for matches
* a regular expression.
*
* Checks a given value using the Perl Compatible Regular Expression extension
* in PHP. The pattern is matched by executing preg_match().
*
* The pattern string passed in the constructor.
*/
class RegularExpression extends Constraint
{
/**
* @var string
*/
private $pattern;
public function __construct(string $pattern)
{
$this->pattern = $pattern;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return \sprintf(
'matches PCRE pattern "%s"',
$this->pattern
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \preg_match($this->pattern, $other) > 0;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory as ComparatorFactory;
/**
* Constraint that checks if one value is equal to another.
*
* Equality is checked with PHP's == operator, the operator is explained in
* detail at {@url https://php.net/manual/en/types.comparisons.php}.
* Two values are equal if they have the same value disregarding type.
*
* The expected value is passed in the constructor.
*/
final class IsEqual extends Constraint
{
/**
* @var mixed
*/
private $value;
/**
* @var float
*/
private $delta;
/**
* @var bool
*/
private $canonicalize;
/**
* @var bool
*/
private $ignoreCase;
public function __construct($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false)
{
$this->value = $value;
$this->delta = $delta;
$this->canonicalize = $canonicalize;
$this->ignoreCase = $ignoreCase;
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
// If $this->value and $other are identical, they are also equal.
// This is the most common path and will allow us to skip
// initialization of all the comparators.
if ($this->value === $other) {
return true;
}
$comparatorFactory = ComparatorFactory::getInstance();
try {
$comparator = $comparatorFactory->getComparatorFor(
$this->value,
$other
);
$comparator->assertEquals(
$this->value,
$other,
$this->delta,
$this->canonicalize,
$this->ignoreCase
);
} catch (ComparisonFailure $f) {
if ($returnResult) {
return false;
}
throw new ExpectationFailedException(
\trim($description . "\n" . $f->getMessage()),
$f
);
}
return true;
}
/**
* Returns a string representation of the constraint.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
$delta = '';
if (\is_string($this->value)) {
if (\strpos($this->value, "\n") !== false) {
return 'is equal to <text>';
}
return \sprintf(
"is equal to '%s'",
$this->value
);
}
if ($this->delta != 0) {
$delta = \sprintf(
' with delta <%F>',
$this->delta
);
}
return \sprintf(
'is equal to %s%s',
$this->exporter()->export($this->value),
$delta
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Provides human readable messages for each JSON error.
*/
final class JsonMatchesErrorMessageProvider
{
/**
* Translates JSON error to a human readable string.
*/
public static function determineJsonError(string $error, string $prefix = ''): ?string
{
switch ($error) {
case \JSON_ERROR_NONE:
return null;
case \JSON_ERROR_DEPTH:
return $prefix . 'Maximum stack depth exceeded';
case \JSON_ERROR_STATE_MISMATCH:
return $prefix . 'Underflow or the modes mismatch';
case \JSON_ERROR_CTRL_CHAR:
return $prefix . 'Unexpected control character found';
case \JSON_ERROR_SYNTAX:
return $prefix . 'Syntax error, malformed JSON';
case \JSON_ERROR_UTF8:
return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded';
default:
return $prefix . 'Unknown error';
}
}
/**
* Translates a given type to a human readable message prefix.
*/
public static function translateTypeToPrefix(string $type): string
{
switch (\strtolower($type)) {
case 'expected':
$prefix = 'Expected value JSON decode error - ';
break;
case 'actual':
$prefix = 'Actual value JSON decode error - ';
break;
default:
$prefix = '';
break;
}
return $prefix;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use Countable;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\SelfDescribing;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Exporter\Exporter;
/**
* Abstract base class for constraints which can be applied to any value.
*/
abstract class Constraint implements Countable, SelfDescribing
{
/**
* @var Exporter
*/
private $exporter;
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
$success = false;
if ($this->matches($other)) {
$success = true;
}
if ($returnResult) {
return $success;
}
if (!$success) {
$this->fail($other, $description);
}
}
/**
* Counts the number of constraint elements.
*/
public function count(): int
{
return 1;
}
protected function exporter(): Exporter
{
if ($this->exporter === null) {
$this->exporter = new Exporter;
}
return $this->exporter;
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* This method can be overridden to implement the evaluation algorithm.
*
* @param mixed $other value or object to evaluate
* @codeCoverageIgnore
*/
protected function matches($other): bool
{
return false;
}
/**
* Throws an exception for the given compared value and test description
*
* @param mixed $other evaluated value or object
* @param string $description Additional information about the test
* @param ComparisonFailure $comparisonFailure
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function fail($other, $description, ComparisonFailure $comparisonFailure = null): void
{
$failureDescription = \sprintf(
'Failed asserting that %s.',
$this->failureDescription($other)
);
$additionalFailureDescription = $this->additionalFailureDescription($other);
if ($additionalFailureDescription) {
$failureDescription .= "\n" . $additionalFailureDescription;
}
if (!empty($description)) {
$failureDescription = $description . "\n" . $failureDescription;
}
throw new ExpectationFailedException(
$failureDescription,
$comparisonFailure
);
}
/**
* Return additional failure description where needed
*
* The function can be overridden to provide additional failure
* information like a diff
*
* @param mixed $other evaluated value or object
*/
protected function additionalFailureDescription($other): string
{
return '';
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* To provide additional failure information additionalFailureDescription
* can be used.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
return $this->exporter()->export($other) . ' ' . $this->toString();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that accepts infinite.
*/
final class IsInfinite extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is infinite';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \is_infinite($other);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that asserts that the string it is evaluated for contains
* a given string.
*
* Uses mb_strpos() to find the position of the string in the input, if not
* found the evaluation fails.
*
* The sub-string is passed in the constructor.
*/
final class StringContains extends Constraint
{
/**
* @var string
*/
private $string;
/**
* @var bool
*/
private $ignoreCase;
public function __construct(string $string, bool $ignoreCase = false)
{
$this->string = $string;
$this->ignoreCase = $ignoreCase;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
if ($this->ignoreCase) {
$string = \mb_strtolower($this->string);
} else {
$string = $this->string;
}
return \sprintf(
'contains "%s"',
$string
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
if ('' === $this->string) {
return true;
}
if ($this->ignoreCase) {
return \mb_stripos($other, $this->string) !== false;
}
return \mb_strpos($other, $this->string) !== false;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
/**
* Logical OR.
*/
final class LogicalOr extends Constraint
{
/**
* @var Constraint[]
*/
private $constraints = [];
public static function fromConstraints(Constraint ...$constraints): self
{
$constraint = new self;
$constraint->constraints = \array_values($constraints);
return $constraint;
}
/**
* @param Constraint[] $constraints
*/
public function setConstraints(array $constraints): void
{
$this->constraints = [];
foreach ($constraints as $constraint) {
if (!($constraint instanceof Constraint)) {
$constraint = new IsEqual(
$constraint
);
}
$this->constraints[] = $constraint;
}
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
$success = false;
foreach ($this->constraints as $constraint) {
if ($constraint->evaluate($other, $description, true)) {
$success = true;
break;
}
}
if ($returnResult) {
return $success;
}
if (!$success) {
$this->fail($other, $description);
}
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
$text = '';
foreach ($this->constraints as $key => $constraint) {
if ($key > 0) {
$text .= ' or ';
}
$text .= $constraint->toString();
}
return $text;
}
/**
* Counts the number of constraint elements.
*/
public function count(): int
{
$count = 0;
foreach ($this->constraints as $constraint) {
$count += \count($constraint);
}
return $count;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that evaluates against a specified closure.
*/
final class Callback extends Constraint
{
/**
* @var callable
*/
private $callback;
public function __construct(callable $callback)
{
$this->callback = $callback;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is accepted by specified callback';
}
/**
* Evaluates the constraint for parameter $value. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \call_user_func($this->callback, $other);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Util\Json;
use SebastianBergmann\Comparator\ComparisonFailure;
/**
* Asserts whether or not two JSON objects are equal.
*/
final class JsonMatches extends Constraint
{
/**
* @var string
*/
private $value;
public function __construct(string $value)
{
$this->value = $value;
}
/**
* Returns a string representation of the object.
*/
public function toString(): string
{
return \sprintf(
'matches JSON string "%s"',
$this->value
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* This method can be overridden to implement the evaluation algorithm.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
[$error, $recodedOther] = Json::canonicalize($other);
if ($error) {
return false;
}
[$error, $recodedValue] = Json::canonicalize($this->value);
if ($error) {
return false;
}
return $recodedOther == $recodedValue;
}
/**
* Throws an exception for the given compared value and test description
*
* @param mixed $other evaluated value or object
* @param string $description Additional information about the test
* @param ComparisonFailure $comparisonFailure
*
* @throws ExpectationFailedException
* @throws \PHPUnit\Framework\Exception
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function fail($other, $description, ComparisonFailure $comparisonFailure = null): void
{
if ($comparisonFailure === null) {
[$error] = Json::canonicalize($other);
if ($error) {
parent::fail($other, $description);
}
[$error] = Json::canonicalize($this->value);
if ($error) {
parent::fail($other, $description);
}
$comparisonFailure = new ComparisonFailure(
\json_decode($this->value),
\json_decode($other),
Json::prettify($this->value),
Json::prettify($other),
false,
'Failed asserting that two json values are equal.'
);
}
parent::fail($other, $description, $comparisonFailure);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that accepts true.
*/
final class IsTrue extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is true';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return $other === true;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
/**
* Logical NOT.
*/
final class LogicalNot extends Constraint
{
/**
* @var Constraint
*/
private $constraint;
public static function negate(string $string): string
{
$positives = [
'contains ',
'exists',
'has ',
'is ',
'are ',
'matches ',
'starts with ',
'ends with ',
'reference ',
'not not ',
];
$negatives = [
'does not contain ',
'does not exist',
'does not have ',
'is not ',
'are not ',
'does not match ',
'starts not with ',
'ends not with ',
'don\'t reference ',
'not ',
];
\preg_match('/(\'[\w\W]*\')([\w\W]*)("[\w\W]*")/i', $string, $matches);
if (\count($matches) > 0) {
$nonInput = $matches[2];
$negatedString = \str_replace(
$nonInput,
\str_replace(
$positives,
$negatives,
$nonInput
),
$string
);
} else {
$negatedString = \str_replace(
$positives,
$negatives,
$string
);
}
return $negatedString;
}
/**
* @param Constraint|mixed $constraint
*/
public function __construct($constraint)
{
if (!($constraint instanceof Constraint)) {
$constraint = new IsEqual($constraint);
}
$this->constraint = $constraint;
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
$success = !$this->constraint->evaluate($other, $description, true);
if ($returnResult) {
return $success;
}
if (!$success) {
$this->fail($other, $description);
}
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
switch (\get_class($this->constraint)) {
case LogicalAnd::class:
case self::class:
case LogicalOr::class:
return 'not( ' . $this->constraint->toString() . ' )';
default:
return self::negate(
$this->constraint->toString()
);
}
}
/**
* Counts the number of constraint elements.
*/
public function count(): int
{
return \count($this->constraint);
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
switch (\get_class($this->constraint)) {
case LogicalAnd::class:
case self::class:
case LogicalOr::class:
return 'not( ' . $this->constraint->failureDescription($other) . ' )';
default:
return self::negate(
$this->constraint->failureDescription($other)
);
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
final class ExceptionCode extends Constraint
{
/**
* @var int|string
*/
private $expectedCode;
/**
* @param int|string $expected
*/
public function __construct($expected)
{
$this->expectedCode = $expected;
}
public function toString(): string
{
return 'exception code is ';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param \Throwable $other
*/
protected function matches($other): bool
{
return (string) $other->getCode() === (string) $this->expectedCode;
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
return \sprintf(
'%s is equal to expected exception code %s',
$this->exporter()->export($other->getCode()),
$this->exporter()->export($this->expectedCode)
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use ReflectionClass;
use ReflectionException;
/**
* Constraint that asserts that the object it is evaluated for is an instance
* of a given class.
*
* The expected class name is passed in the constructor.
*/
final class IsInstanceOf extends Constraint
{
/**
* @var string
*/
private $className;
public function __construct(string $className)
{
$this->className = $className;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return \sprintf(
'is instance of %s "%s"',
$this->getType(),
$this->className
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return $other instanceof $this->className;
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
return \sprintf(
'%s is an instance of %s "%s"',
$this->exporter()->shortenedExport($other),
$this->getType(),
$this->className
);
}
private function getType(): string
{
try {
$reflection = new ReflectionClass($this->className);
if ($reflection->isInterface()) {
return 'interface';
}
} catch (ReflectionException $e) {
}
return 'class';
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that asserts that a string is valid JSON.
*/
final class IsJson extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is valid JSON';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
if ($other === '') {
return false;
}
\json_decode($other);
if (\json_last_error()) {
return false;
}
return true;
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
if ($other === '') {
return 'an empty string is valid JSON';
}
\json_decode($other);
$error = JsonMatchesErrorMessageProvider::determineJsonError(
(string) \json_last_error()
);
return \sprintf(
'%s is valid JSON (%s)',
$this->exporter()->shortenedExport($other),
$error
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use ArrayAccess;
/**
* Constraint that asserts that the array it is evaluated for has a given key.
*
* Uses array_key_exists() to check if the key is found in the input array, if
* not found the evaluation fails.
*
* The array key is passed in the constructor.
*/
final class ArrayHasKey extends Constraint
{
/**
* @var int|string
*/
private $key;
/**
* @param int|string $key
*/
public function __construct($key)
{
$this->key = $key;
}
/**
* Returns a string representation of the constraint.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
return 'has the key ' . $this->exporter()->export($this->key);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
if (\is_array($other)) {
return \array_key_exists($this->key, $other);
}
if ($other instanceof ArrayAccess) {
return $other->offsetExists($this->key);
}
return false;
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
return 'an array ' . $this->toString();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that asserts that the string it is evaluated for ends with a given
* suffix.
*/
final class StringEndsWith extends Constraint
{
/**
* @var string
*/
private $suffix;
public function __construct(string $suffix)
{
$this->suffix = $suffix;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'ends with "' . $this->suffix . '"';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \substr($other, 0 - \strlen($this->suffix)) === $this->suffix;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
/**
* Constraint that asserts that the array it is evaluated for has a specified subset.
*
* Uses array_replace_recursive() to check if a key value subset is part of the
* subject array.
*
* @codeCoverageIgnore
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494
*/
final class ArraySubset extends Constraint
{
/**
* @var iterable
*/
private $subset;
/**
* @var bool
*/
private $strict;
public function __construct(iterable $subset, bool $strict = false)
{
$this->strict = $strict;
$this->subset = $subset;
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
//type cast $other & $this->subset as an array to allow
//support in standard array functions.
$other = $this->toArray($other);
$this->subset = $this->toArray($this->subset);
$patched = \array_replace_recursive($other, $this->subset);
if ($this->strict) {
$result = $other === $patched;
} else {
$result = $other == $patched;
}
if ($returnResult) {
return $result;
}
if (!$result) {
$f = new ComparisonFailure(
$patched,
$other,
\var_export($patched, true),
\var_export($other, true)
);
$this->fail($other, $description, $f);
}
}
/**
* Returns a string representation of the constraint.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
return 'has the subset ' . $this->exporter()->export($this->subset);
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
return 'an array ' . $this->toString();
}
private function toArray(iterable $other): array
{
if (\is_array($other)) {
return $other;
}
if ($other instanceof \ArrayObject) {
return $other->getArrayCopy();
}
if ($other instanceof \Traversable) {
return \iterator_to_array($other);
}
// Keep BC even if we know that array would not be the expected one
return (array) $other;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that accepts null.
*/
final class IsNull extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is null';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return $other === null;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Util\Filter;
use Throwable;
final class Exception extends Constraint
{
/**
* @var string
*/
private $className;
public function __construct(string $className)
{
$this->className = $className;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return \sprintf(
'exception of type "%s"',
$this->className
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return $other instanceof $this->className;
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
if ($other !== null) {
$message = '';
if ($other instanceof Throwable) {
$message = '. Message was: "' . $other->getMessage() . '" at'
. "\n" . Filter::getFilteredStacktrace($other);
}
return \sprintf(
'exception of type "%s" matches expected exception "%s"%s',
\get_class($other),
$this->className,
$message
);
}
return \sprintf(
'exception of type "%s" is thrown',
$this->className
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that asserts that the value it is evaluated for is greater
* than a given value.
*/
final class GreaterThan extends Constraint
{
/**
* @var float|int
*/
private $value;
/**
* @param float|int $value
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* Returns a string representation of the constraint.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
return 'is greater than ' . $this->exporter()->export($this->value);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return $this->value < $other;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that asserts that the value it is evaluated for is less than
* a given value.
*/
final class LessThan extends Constraint
{
/**
* @var float|int
*/
private $value;
/**
* @param float|int $value
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* Returns a string representation of the constraint.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
return 'is less than ' . $this->exporter()->export($this->value);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return $this->value > $other;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that checks if the file(name) that it is evaluated for exists.
*
* The file path to check is passed as $other in evaluate().
*/
final class FileExists extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'file exists';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \file_exists($other);
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
return \sprintf(
'file "%s" exists',
$other
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that accepts finite.
*/
final class IsFinite extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is finite';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \is_finite($other);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
/**
* Constraint that asserts that one value is identical to another.
*
* Identical check is performed with PHP's === operator, the operator is
* explained in detail at
* {@url https://php.net/manual/en/types.comparisons.php}.
* Two values are identical if they have the same value and are of the same
* type.
*
* The expected value is passed in the constructor.
*/
final class IsIdentical extends Constraint
{
/**
* @var float
*/
private const EPSILON = 0.0000000001;
/**
* @var mixed
*/
private $value;
public function __construct($value)
{
$this->value = $value;
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
if (\is_float($this->value) && \is_float($other) &&
!\is_infinite($this->value) && !\is_infinite($other) &&
!\is_nan($this->value) && !\is_nan($other)) {
$success = \abs($this->value - $other) < self::EPSILON;
} else {
$success = $this->value === $other;
}
if ($returnResult) {
return $success;
}
if (!$success) {
$f = null;
// if both values are strings, make sure a diff is generated
if (\is_string($this->value) && \is_string($other)) {
$f = new ComparisonFailure(
$this->value,
$other,
\sprintf("'%s'", $this->value),
\sprintf("'%s'", $other)
);
}
// if both values are array, make sure a diff is generated
if (\is_array($this->value) && \is_array($other)) {
$f = new ComparisonFailure(
$this->value,
$other,
$this->exporter()->export($this->value),
$this->exporter()->export($other)
);
}
$this->fail($other, $description, $f);
}
}
/**
* Returns a string representation of the constraint.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
if (\is_object($this->value)) {
return 'is identical to an object of class "' .
\get_class($this->value) . '"';
}
return 'is identical to ' . $this->exporter()->export($this->value);
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
if (\is_object($this->value) && \is_object($other)) {
return 'two variables reference the same object';
}
if (\is_string($this->value) && \is_string($other)) {
return 'two strings are identical';
}
if (\is_array($this->value) && \is_array($other)) {
return 'two arrays are identical';
}
return parent::failureDescription($other);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use Countable;
/**
* Constraint that checks whether a variable is empty().
*/
final class IsEmpty extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is empty';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
if ($other instanceof \EmptyIterator) {
return true;
}
if ($other instanceof Countable) {
return \count($other) === 0;
}
return empty($other);
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
$type = \gettype($other);
return \sprintf(
'%s %s %s',
\strpos($type, 'a') === 0 || \strpos($type, 'o') === 0 ? 'an' : 'a',
$type,
$this->toString()
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
/**
* Constraint that asserts that the Traversable it is applied to contains
* only values of a given type.
*/
final class TraversableContainsOnly extends Constraint
{
/**
* @var Constraint
*/
private $constraint;
/**
* @var string
*/
private $type;
/**
* @throws \PHPUnit\Framework\Exception
*/
public function __construct(string $type, bool $isNativeType = true)
{
if ($isNativeType) {
$this->constraint = new IsType($type);
} else {
$this->constraint = new IsInstanceOf(
$type
);
}
$this->type = $type;
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
$success = true;
foreach ($other as $item) {
if (!$this->constraint->evaluate($item, '', true)) {
$success = false;
break;
}
}
if ($returnResult) {
return $success;
}
if (!$success) {
$this->fail($other, $description);
}
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'contains only values of type "' . $this->type . '"';
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
final class ExceptionMessage extends Constraint
{
/**
* @var string
*/
private $expectedMessage;
public function __construct(string $expected)
{
$this->expectedMessage = $expected;
}
public function toString(): string
{
if ($this->expectedMessage === '') {
return 'exception message is empty';
}
return 'exception message contains ';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param \Throwable $other
*/
protected function matches($other): bool
{
if ($this->expectedMessage === '') {
return $other->getMessage() === '';
}
return \strpos((string) $other->getMessage(), $this->expectedMessage) !== false;
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
if ($this->expectedMessage === '') {
return \sprintf(
"exception message is empty but is '%s'",
$other->getMessage()
);
}
return \sprintf(
"exception message '%s' contains '%s'",
$other->getMessage(),
$this->expectedMessage
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\Exception;
use ReflectionClass;
/**
* Constraint that asserts that the class it is evaluated for has a given
* static attribute.
*
* The attribute name is passed in the constructor.
*/
final class ClassHasStaticAttribute extends ClassHasAttribute
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return \sprintf(
'has static attribute "%s"',
$this->attributeName()
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
try {
$class = new ReflectionClass($other);
if ($class->hasProperty($this->attributeName())) {
return $class->getProperty($this->attributeName())->isStatic();
}
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
return false;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that checks if the file/dir(name) that it is evaluated for is writable.
*
* The file path to check is passed as $other in evaluate().
*/
final class IsWritable extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is writable';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \is_writable($other);
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
return \sprintf(
'"%s" is writable',
$other
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\ExpectationFailedException;
/**
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
final class Attribute extends Composite
{
/**
* @var string
*/
private $attributeName;
public function __construct(Constraint $constraint, string $attributeName)
{
parent::__construct($constraint);
$this->attributeName = $attributeName;
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \PHPUnit\Framework\Exception
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
return parent::evaluate(
Assert::readAttribute(
$other,
$this->attributeName
),
$description,
$returnResult
);
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'attribute "' . $this->attributeName . '" ' . $this->innerConstraint()->toString();
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
return $this->toString();
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use ReflectionObject;
/**
* Constraint that asserts that the object it is evaluated for has a given
* attribute.
*
* The attribute name is passed in the constructor.
*/
final class ObjectHasAttribute extends ClassHasAttribute
{
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return (new ReflectionObject($other))->hasProperty($this->attributeName());
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that checks if the directory(name) that it is evaluated for exists.
*
* The file path to check is passed as $other in evaluate().
*/
final class DirectoryExists extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'directory exists';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \is_dir($other);
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
return \sprintf(
'directory "%s" exists',
$other
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use SebastianBergmann\Diff\Differ;
/**
* ...
*/
final class StringMatchesFormatDescription extends RegularExpression
{
/**
* @var string
*/
private $string;
public function __construct(string $string)
{
parent::__construct(
$this->createPatternFromFormat(
$this->convertNewlines($string)
)
);
$this->string = $string;
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return parent::matches(
$this->convertNewlines($other)
);
}
protected function failureDescription($other): string
{
return 'string matches format description';
}
protected function additionalFailureDescription($other): string
{
$from = \explode("\n", $this->string);
$to = \explode("\n", $this->convertNewlines($other));
foreach ($from as $index => $line) {
if (isset($to[$index]) && $line !== $to[$index]) {
$line = $this->createPatternFromFormat($line);
if (\preg_match($line, $to[$index]) > 0) {
$from[$index] = $to[$index];
}
}
}
$this->string = \implode("\n", $from);
$other = \implode("\n", $to);
return (new Differ("--- Expected\n+++ Actual\n"))->diff($this->string, $other);
}
private function createPatternFromFormat(string $string): string
{
$string = \strtr(
\preg_quote($string, '/'),
[
'%%' => '%',
'%e' => '\\' . \DIRECTORY_SEPARATOR,
'%s' => '[^\r\n]+',
'%S' => '[^\r\n]*',
'%a' => '.+',
'%A' => '.*',
'%w' => '\s*',
'%i' => '[+-]?\d+',
'%d' => '\d+',
'%x' => '[0-9a-fA-F]+',
'%f' => '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?',
'%c' => '.',
]
);
return '/^' . $string . '$/s';
}
private function convertNewlines($text): string
{
return \preg_replace('/\r\n/', "\n", $text);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
/**
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*/
abstract class Composite extends Constraint
{
/**
* @var Constraint
*/
private $innerConstraint;
public function __construct(Constraint $innerConstraint)
{
$this->innerConstraint = $innerConstraint;
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
try {
return $this->innerConstraint->evaluate(
$other,
$description,
$returnResult
);
} catch (ExpectationFailedException $e) {
$this->fail($other, $description, $e->getComparisonFailure());
}
}
/**
* Counts the number of constraint elements.
*/
public function count(): int
{
return \count($this->innerConstraint);
}
protected function innerConstraint(): Constraint
{
return $this->innerConstraint;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that accepts false.
*/
final class IsFalse extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is false';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return $other === false;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
/**
* Logical AND.
*/
final class LogicalAnd extends Constraint
{
/**
* @var Constraint[]
*/
private $constraints = [];
public static function fromConstraints(Constraint ...$constraints): self
{
$constraint = new self;
$constraint->constraints = \array_values($constraints);
return $constraint;
}
/**
* @param Constraint[] $constraints
*
* @throws \PHPUnit\Framework\Exception
*/
public function setConstraints(array $constraints): void
{
$this->constraints = [];
foreach ($constraints as $constraint) {
if (!($constraint instanceof Constraint)) {
throw new \PHPUnit\Framework\Exception(
'All parameters to ' . __CLASS__ .
' must be a constraint object.'
);
}
$this->constraints[] = $constraint;
}
}
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
$success = true;
foreach ($this->constraints as $constraint) {
if (!$constraint->evaluate($other, $description, true)) {
$success = false;
break;
}
}
if ($returnResult) {
return $success;
}
if (!$success) {
$this->fail($other, $description);
}
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
$text = '';
foreach ($this->constraints as $key => $constraint) {
if ($key > 0) {
$text .= ' and ';
}
$text .= $constraint->toString();
}
return $text;
}
/**
* Counts the number of constraint elements.
*/
public function count(): int
{
$count = 0;
foreach ($this->constraints as $constraint) {
$count += \count($constraint);
}
return $count;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Util\RegularExpression as RegularExpressionUtil;
final class ExceptionMessageRegularExpression extends Constraint
{
/**
* @var string
*/
private $expectedMessageRegExp;
public function __construct(string $expected)
{
$this->expectedMessageRegExp = $expected;
}
public function toString(): string
{
return 'exception message matches ';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param \PHPUnit\Framework\Exception $other
*
* @throws \Exception
* @throws \PHPUnit\Framework\Exception
*/
protected function matches($other): bool
{
$match = RegularExpressionUtil::safeMatch($this->expectedMessageRegExp, $other->getMessage());
if ($match === false) {
throw new \PHPUnit\Framework\Exception(
"Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'"
);
}
return $match === 1;
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
return \sprintf(
"exception message '%s' matches '%s'",
$other->getMessage(),
$this->expectedMessageRegExp
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use Countable;
use Generator;
use Iterator;
use IteratorAggregate;
use Traversable;
class Count extends Constraint
{
/**
* @var int
*/
private $expectedCount;
public function __construct(int $expected)
{
$this->expectedCount = $expected;
}
public function toString(): string
{
return \sprintf(
'count matches %d',
$this->expectedCount
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*/
protected function matches($other): bool
{
return $this->expectedCount === $this->getCountOf($other);
}
/**
* @param iterable $other
*/
protected function getCountOf($other): ?int
{
if ($other instanceof Countable || \is_array($other)) {
return \count($other);
}
if ($other instanceof \EmptyIterator) {
return 0;
}
if ($other instanceof Traversable) {
while ($other instanceof IteratorAggregate) {
$other = $other->getIterator();
}
$iterator = $other;
if ($iterator instanceof Generator) {
return $this->getCountOfGenerator($iterator);
}
if (!$iterator instanceof Iterator) {
return \iterator_count($iterator);
}
$key = $iterator->key();
$count = \iterator_count($iterator);
// Manually rewind $iterator to previous key, since iterator_count
// moves pointer.
if ($key !== null) {
$iterator->rewind();
while ($iterator->valid() && $key !== $iterator->key()) {
$iterator->next();
}
}
return $count;
}
return null;
}
/**
* Returns the total number of iterations from a generator.
* This will fully exhaust the generator.
*/
protected function getCountOfGenerator(Generator $generator): int
{
for ($count = 0; $generator->valid(); $generator->next()) {
++$count;
}
return $count;
}
/**
* Returns the description of the failure.
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
return \sprintf(
'actual size %d matches expected size %d',
$this->getCountOf($other),
$this->expectedCount
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that asserts that the value it is evaluated for is of a
* specified type.
*
* The expected value is passed in the constructor.
*/
final class IsType extends Constraint
{
/**
* @var string
*/
public const TYPE_ARRAY = 'array';
/**
* @var string
*/
public const TYPE_BOOL = 'bool';
/**
* @var string
*/
public const TYPE_FLOAT = 'float';
/**
* @var string
*/
public const TYPE_INT = 'int';
/**
* @var string
*/
public const TYPE_NULL = 'null';
/**
* @var string
*/
public const TYPE_NUMERIC = 'numeric';
/**
* @var string
*/
public const TYPE_OBJECT = 'object';
/**
* @var string
*/
public const TYPE_RESOURCE = 'resource';
/**
* @var string
*/
public const TYPE_STRING = 'string';
/**
* @var string
*/
public const TYPE_SCALAR = 'scalar';
/**
* @var string
*/
public const TYPE_CALLABLE = 'callable';
/**
* @var string
*/
public const TYPE_ITERABLE = 'iterable';
/**
* @var array<string,bool>
*/
private const KNOWN_TYPES = [
'array' => true,
'boolean' => true,
'bool' => true,
'double' => true,
'float' => true,
'integer' => true,
'int' => true,
'null' => true,
'numeric' => true,
'object' => true,
'real' => true,
'resource' => true,
'string' => true,
'scalar' => true,
'callable' => true,
'iterable' => true,
];
/**
* @var string
*/
private $type;
/**
* @throws \PHPUnit\Framework\Exception
*/
public function __construct(string $type)
{
if (!isset(self::KNOWN_TYPES[$type])) {
throw new \PHPUnit\Framework\Exception(
\sprintf(
'Type specified for PHPUnit\Framework\Constraint\IsType <%s> ' .
'is not a valid type.',
$type
)
);
}
$this->type = $type;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return \sprintf(
'is of type "%s"',
$this->type
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
switch ($this->type) {
case 'numeric':
return \is_numeric($other);
case 'integer':
case 'int':
return \is_int($other);
case 'double':
case 'float':
case 'real':
return \is_float($other);
case 'string':
return \is_string($other);
case 'boolean':
case 'bool':
return \is_bool($other);
case 'null':
return null === $other;
case 'array':
return \is_array($other);
case 'object':
return \is_object($other);
case 'resource':
if (\is_resource($other)) {
return true;
}
try {
$resource = @\get_resource_type($other);
if (\is_string($resource)) {
return true;
}
} catch (\TypeError $e) {
}
return false;
case 'scalar':
return \is_scalar($other);
case 'callable':
return \is_callable($other);
case 'iterable':
return \is_iterable($other);
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use SplObjectStorage;
/**
* Constraint that asserts that the Traversable it is applied to contains
* a given value.
*/
final class TraversableContains extends Constraint
{
/**
* @var bool
*/
private $checkForObjectIdentity;
/**
* @var bool
*/
private $checkForNonObjectIdentity;
/**
* @var mixed
*/
private $value;
/**
* @throws \PHPUnit\Framework\Exception
*/
public function __construct($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false)
{
$this->checkForObjectIdentity = $checkForObjectIdentity;
$this->checkForNonObjectIdentity = $checkForNonObjectIdentity;
$this->value = $value;
}
/**
* Returns a string representation of the constraint.
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function toString(): string
{
if (\is_string($this->value) && \strpos($this->value, "\n") !== false) {
return 'contains "' . $this->value . '"';
}
return 'contains ' . $this->exporter()->export($this->value);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
if ($other instanceof SplObjectStorage) {
return $other->contains($this->value);
}
if (\is_object($this->value)) {
foreach ($other as $element) {
if ($this->checkForObjectIdentity && $element === $this->value) {
return true;
}
if (!$this->checkForObjectIdentity && $element == $this->value) {
return true;
}
}
} else {
foreach ($other as $element) {
if ($this->checkForNonObjectIdentity && $element === $this->value) {
return true;
}
if (!$this->checkForNonObjectIdentity && $element == $this->value) {
return true;
}
}
}
return false;
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
protected function failureDescription($other): string
{
return \sprintf(
'%s %s',
\is_array($other) ? 'an array' : 'a traversable',
$this->toString()
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that accepts nan.
*/
final class IsNan extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is nan';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \is_nan($other);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\Exception;
use ReflectionClass;
/**
* Constraint that asserts that the class it is evaluated for has a given
* attribute.
*
* The attribute name is passed in the constructor.
*/
class ClassHasAttribute extends Constraint
{
/**
* @var string
*/
private $attributeName;
public function __construct(string $attributeName)
{
$this->attributeName = $attributeName;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return \sprintf(
'has attribute "%s"',
$this->attributeName
);
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
try {
return (new ReflectionClass($other))->hasProperty($this->attributeName);
} catch (\ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
return \sprintf(
'%sclass "%s" %s',
\is_object($other) ? 'object of ' : '',
\is_object($other) ? \get_class($other) : $other,
$this->toString()
);
}
protected function attributeName(): string
{
return $this->attributeName;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
/**
* Constraint that checks if the file/dir(name) that it is evaluated for is readable.
*
* The file path to check is passed as $other in evaluate().
*/
final class IsReadable extends Constraint
{
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is readable';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \is_readable($other);
}
/**
* Returns the description of the failure
*
* The beginning of failure messages is "Failed asserting that" in most
* cases. This method should return the second part of that sentence.
*
* @param mixed $other evaluated value or object
*/
protected function failureDescription($other): string
{
return \sprintf(
'"%s" is readable',
$other
);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\InvalidArgumentException;
/**
* Constraint that asserts that the string it is evaluated for begins with a
* given prefix.
*/
final class StringStartsWith extends Constraint
{
/**
* @var string
*/
private $prefix;
public function __construct(string $prefix)
{
if (\strlen($prefix) === 0) {
throw InvalidArgumentException::create(1, 'non-empty string');
}
$this->prefix = $prefix;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'starts with "' . $this->prefix . '"';
}
/**
* Evaluates the constraint for parameter $other. Returns true if the
* constraint is met, false otherwise.
*
* @param mixed $other value or object to evaluate
*/
protected function matches($other): bool
{
return \strpos((string) $other, $this->prefix) === 0;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
final class SameSize extends Count
{
public function __construct(iterable $expected)
{
parent::__construct($this->getCountOf($expected));
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
/**
* Constraint that accepts any input value.
*/
final class IsAnything extends Constraint
{
/**
* Evaluates the constraint for parameter $other
*
* If $returnResult is set to false (the default), an exception is thrown
* in case of a failure. null is returned otherwise.
*
* If $returnResult is true, the result of the evaluation is returned as
* a boolean value instead: true in case of success, false in case of a
* failure.
*
* @throws ExpectationFailedException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
return $returnResult ? true : null;
}
/**
* Returns a string representation of the constraint.
*/
public function toString(): string
{
return 'is anything';
}
/**
* Counts the number of constraint elements.
*/
public function count(): int
{
return 0;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use PHPUnit\Framework\Error\Error;
use Throwable;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TestFailure
{
/**
* @var null|Test
*/
private $failedTest;
/**
* @var Throwable
*/
private $thrownException;
/**
* @var string
*/
private $testName;
/**
* Returns a description for an exception.
*/
public static function exceptionToString(Throwable $e): string
{
if ($e instanceof SelfDescribing) {
$buffer = $e->toString();
if ($e instanceof ExpectationFailedException && $e->getComparisonFailure()) {
$buffer .= $e->getComparisonFailure()->getDiff();
}
if ($e instanceof PHPTAssertionFailedError) {
$buffer .= $e->getDiff();
}
if (!empty($buffer)) {
$buffer = \trim($buffer) . "\n";
}
return $buffer;
}
if ($e instanceof Error) {
return $e->getMessage() . "\n";
}
if ($e instanceof ExceptionWrapper) {
return $e->getClassName() . ': ' . $e->getMessage() . "\n";
}
return \get_class($e) . ': ' . $e->getMessage() . "\n";
}
/**
* Constructs a TestFailure with the given test and exception.
*
* @param Throwable $t
*/
public function __construct(Test $failedTest, $t)
{
if ($failedTest instanceof SelfDescribing) {
$this->testName = $failedTest->toString();
} else {
$this->testName = \get_class($failedTest);
}
if (!$failedTest instanceof TestCase || !$failedTest->isInIsolation()) {
$this->failedTest = $failedTest;
}
$this->thrownException = $t;
}
/**
* Returns a short description of the failure.
*/
public function toString(): string
{
return \sprintf(
'%s: %s',
$this->testName,
$this->thrownException->getMessage()
);
}
/**
* Returns a description for the thrown exception.
*/
public function getExceptionAsString(): string
{
return self::exceptionToString($this->thrownException);
}
/**
* Returns the name of the failing test (including data set, if any).
*/
public function getTestName(): string
{
return $this->testName;
}
/**
* Returns the failing test.
*
* Note: The test object is not set when the test is executed in process
* isolation.
*
* @see Exception
*/
public function failedTest(): ?Test
{
return $this->failedTest;
}
/**
* Gets the thrown exception.
*/
public function thrownException(): Throwable
{
return $this->thrownException;
}
/**
* Returns the exception's message.
*/
public function exceptionMessage(): string
{
return $this->thrownException()->getMessage();
}
/**
* Returns true if the thrown exception
* is of type AssertionFailedError.
*/
public function isFailure(): bool
{
return $this->thrownException() instanceof AssertionFailedError;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
use RecursiveIterator;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class TestSuiteIterator implements RecursiveIterator
{
/**
* @var int
*/
private $position = 0;
/**
* @var Test[]
*/
private $tests;
public function __construct(TestSuite $testSuite)
{
$this->tests = $testSuite->tests();
}
/**
* 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->tests);
}
/**
* Returns the key of the current element.
*/
public function key(): int
{
return $this->position;
}
/**
* Returns the current element.
*/
public function current(): ?Test
{
return $this->valid() ? $this->tests[$this->position] : null;
}
/**
* Moves forward to next element.
*/
public function next(): void
{
$this->position++;
}
/**
* Returns the sub iterator for the current element.
*
* @throws \UnexpectedValueException if the current element is no TestSuite
*/
public function getChildren(): self
{
if (!$this->hasChildren()) {
throw new UnexpectedValueException(
'The current item is no TestSuite instance and hence cannot have any children.',
1567849414
);
}
/** @var TestSuite $current */
$current = $this->current();
return new self($current);
}
/**
* Checks whether the current element has children.
*/
public function hasChildren(): bool
{
return $this->current() instanceof TestSuite;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Constraint\ArrayHasKey;
use PHPUnit\Framework\Constraint\Attribute;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\Constraint\ClassHasAttribute;
use PHPUnit\Framework\Constraint\ClassHasStaticAttribute;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Constraint\Count;
use PHPUnit\Framework\Constraint\DirectoryExists;
use PHPUnit\Framework\Constraint\FileExists;
use PHPUnit\Framework\Constraint\GreaterThan;
use PHPUnit\Framework\Constraint\IsAnything;
use PHPUnit\Framework\Constraint\IsEmpty;
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\Constraint\IsFalse;
use PHPUnit\Framework\Constraint\IsFinite;
use PHPUnit\Framework\Constraint\IsIdentical;
use PHPUnit\Framework\Constraint\IsInfinite;
use PHPUnit\Framework\Constraint\IsInstanceOf;
use PHPUnit\Framework\Constraint\IsJson;
use PHPUnit\Framework\Constraint\IsNan;
use PHPUnit\Framework\Constraint\IsNull;
use PHPUnit\Framework\Constraint\IsReadable;
use PHPUnit\Framework\Constraint\IsTrue;
use PHPUnit\Framework\Constraint\IsType;
use PHPUnit\Framework\Constraint\IsWritable;
use PHPUnit\Framework\Constraint\LessThan;
use PHPUnit\Framework\Constraint\LogicalAnd;
use PHPUnit\Framework\Constraint\LogicalNot;
use PHPUnit\Framework\Constraint\LogicalOr;
use PHPUnit\Framework\Constraint\LogicalXor;
use PHPUnit\Framework\Constraint\ObjectHasAttribute;
use PHPUnit\Framework\Constraint\RegularExpression;
use PHPUnit\Framework\Constraint\StringContains;
use PHPUnit\Framework\Constraint\StringEndsWith;
use PHPUnit\Framework\Constraint\StringMatchesFormatDescription;
use PHPUnit\Framework\Constraint\StringStartsWith;
use PHPUnit\Framework\Constraint\TraversableContains;
use PHPUnit\Framework\Constraint\TraversableContainsOnly;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount as AnyInvokedCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtIndex as InvokedAtIndexMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount as InvokedAtLeastCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher;
use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher;
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub;
use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub;
use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub;
use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub;
use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub;
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub;
/**
* Asserts that an array has a specified key.
*
* @param int|string $key
* @param array|ArrayAccess $array
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertArrayHasKey
*/
function assertArrayHasKey($key, $array, string $message = ''): void
{
Assert::assertArrayHasKey(...\func_get_args());
}
/**
* Asserts that an array has a specified subset.
*
* @param array|ArrayAccess $subset
* @param array|ArrayAccess $array
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @codeCoverageIgnore
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494
* @see Assert::assertArraySubset
*/
function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void
{
Assert::assertArraySubset(...\func_get_args());
}
/**
* Asserts that an array does not have a specified key.
*
* @param int|string $key
* @param array|ArrayAccess $array
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertArrayNotHasKey
*/
function assertArrayNotHasKey($key, $array, string $message = ''): void
{
Assert::assertArrayNotHasKey(...\func_get_args());
}
/**
* Asserts that a haystack contains a needle.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertContains
*/
function assertContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
{
Assert::assertContains(...\func_get_args());
}
function assertContainsEquals($needle, iterable $haystack, string $message = ''): void
{
Assert::assertContainsEquals(...\func_get_args());
}
/**
* Asserts that a haystack that is stored in a static attribute of a class
* or an attribute of an object contains a needle.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeContains
*/
function assertAttributeContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
{
Assert::assertAttributeContains(...\func_get_args());
}
/**
* Asserts that a haystack does not contain a needle.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertNotContains
*/
function assertNotContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
{
Assert::assertNotContains(...\func_get_args());
}
function assertNotContainsEquals($needle, iterable $haystack, string $message = ''): void
{
Assert::assertNotContainsEquals(...\func_get_args());
}
/**
* Asserts that a haystack that is stored in a static attribute of a class
* or an attribute of an object does not contain a needle.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeNotContains
*/
function assertAttributeNotContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
{
Assert::assertAttributeNotContains(...\func_get_args());
}
/**
* Asserts that a haystack contains only values of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertContainsOnly
*/
function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
{
Assert::assertContainsOnly(...\func_get_args());
}
/**
* Asserts that a haystack contains only instances of a given class name.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertContainsOnlyInstancesOf
*/
function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void
{
Assert::assertContainsOnlyInstancesOf(...\func_get_args());
}
/**
* Asserts that a haystack that is stored in a static attribute of a class
* or an attribute of an object contains only values of a given type.
*
* @param object|string $haystackClassOrObject
* @param bool $isNativeType
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeContainsOnly
*/
function assertAttributeContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void
{
Assert::assertAttributeContainsOnly(...\func_get_args());
}
/**
* Asserts that a haystack does not contain only values of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotContainsOnly
*/
function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
{
Assert::assertNotContainsOnly(...\func_get_args());
}
/**
* Asserts that a haystack that is stored in a static attribute of a class
* or an attribute of an object does not contain only values of a given
* type.
*
* @param object|string $haystackClassOrObject
* @param bool $isNativeType
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeNotContainsOnly
*/
function assertAttributeNotContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void
{
Assert::assertAttributeNotContainsOnly(...\func_get_args());
}
/**
* Asserts the number of elements of an array, Countable or Traversable.
*
* @param Countable|iterable $haystack
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertCount
*/
function assertCount(int $expectedCount, $haystack, string $message = ''): void
{
Assert::assertCount(...\func_get_args());
}
/**
* Asserts the number of elements of an array, Countable or Traversable
* that is stored in an attribute.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeCount
*/
function assertAttributeCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
{
Assert::assertAttributeCount(...\func_get_args());
}
/**
* Asserts the number of elements of an array, Countable or Traversable.
*
* @param Countable|iterable $haystack
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertNotCount
*/
function assertNotCount(int $expectedCount, $haystack, string $message = ''): void
{
Assert::assertNotCount(...\func_get_args());
}
/**
* Asserts the number of elements of an array, Countable or Traversable
* that is stored in an attribute.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeNotCount
*/
function assertAttributeNotCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
{
Assert::assertAttributeNotCount(...\func_get_args());
}
/**
* Asserts that two variables are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertEquals
*/
function assertEquals($expected, $actual, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
{
Assert::assertEquals(...\func_get_args());
}
/**
* Asserts that two variables are equal (canonicalizing).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertEqualsCanonicalizing
*/
function assertEqualsCanonicalizing($expected, $actual, string $message = ''): void
{
Assert::assertEqualsCanonicalizing(...\func_get_args());
}
/**
* Asserts that two variables are equal (ignoring case).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertEqualsIgnoringCase
*/
function assertEqualsIgnoringCase($expected, $actual, string $message = ''): void
{
Assert::assertEqualsIgnoringCase(...\func_get_args());
}
/**
* Asserts that two variables are equal (with delta).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertEqualsWithDelta
*/
function assertEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
{
Assert::assertEqualsWithDelta(...\func_get_args());
}
/**
* Asserts that a variable is equal to an attribute of an object.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeEquals
*/
function assertAttributeEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
{
Assert::assertAttributeEquals(...\func_get_args());
}
/**
* Asserts that two variables are not equal.
*
* @param float $delta
* @param int $maxDepth
* @param bool $canonicalize
* @param bool $ignoreCase
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotEquals
*/
function assertNotEquals($expected, $actual, string $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false): void
{
Assert::assertNotEquals(...\func_get_args());
}
/**
* Asserts that two variables are not equal (canonicalizing).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotEqualsCanonicalizing
*/
function assertNotEqualsCanonicalizing($expected, $actual, string $message = ''): void
{
Assert::assertNotEqualsCanonicalizing(...\func_get_args());
}
/**
* Asserts that two variables are not equal (ignoring case).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotEqualsIgnoringCase
*/
function assertNotEqualsIgnoringCase($expected, $actual, string $message = ''): void
{
Assert::assertNotEqualsIgnoringCase(...\func_get_args());
}
/**
* Asserts that two variables are not equal (with delta).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotEqualsWithDelta
*/
function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
{
Assert::assertNotEqualsWithDelta(...\func_get_args());
}
/**
* Asserts that a variable is not equal to an attribute of an object.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeNotEquals
*/
function assertAttributeNotEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
{
Assert::assertAttributeNotEquals(...\func_get_args());
}
/**
* Asserts that a variable is empty.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert empty $actual
*
* @see Assert::assertEmpty
*/
function assertEmpty($actual, string $message = ''): void
{
Assert::assertEmpty(...\func_get_args());
}
/**
* Asserts that a static attribute of a class or an attribute of an object
* is empty.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeEmpty
*/
function assertAttributeEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
{
Assert::assertAttributeEmpty(...\func_get_args());
}
/**
* Asserts that a variable is not empty.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !empty $actual
*
* @see Assert::assertNotEmpty
*/
function assertNotEmpty($actual, string $message = ''): void
{
Assert::assertNotEmpty(...\func_get_args());
}
/**
* Asserts that a static attribute of a class or an attribute of an object
* is not empty.
*
* @param object|string $haystackClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeNotEmpty
*/
function assertAttributeNotEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
{
Assert::assertAttributeNotEmpty(...\func_get_args());
}
/**
* Asserts that a value is greater than another value.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertGreaterThan
*/
function assertGreaterThan($expected, $actual, string $message = ''): void
{
Assert::assertGreaterThan(...\func_get_args());
}
/**
* Asserts that an attribute is greater than another value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeGreaterThan
*/
function assertAttributeGreaterThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
Assert::assertAttributeGreaterThan(...\func_get_args());
}
/**
* Asserts that a value is greater than or equal to another value.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertGreaterThanOrEqual
*/
function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void
{
Assert::assertGreaterThanOrEqual(...\func_get_args());
}
/**
* Asserts that an attribute is greater than or equal to another value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeGreaterThanOrEqual
*/
function assertAttributeGreaterThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
Assert::assertAttributeGreaterThanOrEqual(...\func_get_args());
}
/**
* Asserts that a value is smaller than another value.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertLessThan
*/
function assertLessThan($expected, $actual, string $message = ''): void
{
Assert::assertLessThan(...\func_get_args());
}
/**
* Asserts that an attribute is smaller than another value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeLessThan
*/
function assertAttributeLessThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
Assert::assertAttributeLessThan(...\func_get_args());
}
/**
* Asserts that a value is smaller than or equal to another value.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertLessThanOrEqual
*/
function assertLessThanOrEqual($expected, $actual, string $message = ''): void
{
Assert::assertLessThanOrEqual(...\func_get_args());
}
/**
* Asserts that an attribute is smaller than or equal to another value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeLessThanOrEqual
*/
function assertAttributeLessThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
Assert::assertAttributeLessThanOrEqual(...\func_get_args());
}
/**
* Asserts that the contents of one file is equal to the contents of another
* file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFileEquals
*/
function assertFileEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
{
Assert::assertFileEquals(...\func_get_args());
}
/**
* Asserts that the contents of one file is not equal to the contents of
* another file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFileNotEquals
*/
function assertFileNotEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
{
Assert::assertFileNotEquals(...\func_get_args());
}
/**
* Asserts that the contents of a string is equal
* to the contents of a file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringEqualsFile
*/
function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
{
Assert::assertStringEqualsFile(...\func_get_args());
}
/**
* Asserts that the contents of a string is not equal
* to the contents of a file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringNotEqualsFile
*/
function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
{
Assert::assertStringNotEqualsFile(...\func_get_args());
}
/**
* Asserts that a file/dir is readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertIsReadable
*/
function assertIsReadable(string $filename, string $message = ''): void
{
Assert::assertIsReadable(...\func_get_args());
}
/**
* Asserts that a file/dir exists and is not readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotIsReadable
*/
function assertNotIsReadable(string $filename, string $message = ''): void
{
Assert::assertNotIsReadable(...\func_get_args());
}
/**
* Asserts that a file/dir exists and is writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertIsWritable
*/
function assertIsWritable(string $filename, string $message = ''): void
{
Assert::assertIsWritable(...\func_get_args());
}
/**
* Asserts that a file/dir exists and is not writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotIsWritable
*/
function assertNotIsWritable(string $filename, string $message = ''): void
{
Assert::assertNotIsWritable(...\func_get_args());
}
/**
* Asserts that a directory exists.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertDirectoryExists
*/
function assertDirectoryExists(string $directory, string $message = ''): void
{
Assert::assertDirectoryExists(...\func_get_args());
}
/**
* Asserts that a directory does not exist.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertDirectoryNotExists
*/
function assertDirectoryNotExists(string $directory, string $message = ''): void
{
Assert::assertDirectoryNotExists(...\func_get_args());
}
/**
* Asserts that a directory exists and is readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertDirectoryIsReadable
*/
function assertDirectoryIsReadable(string $directory, string $message = ''): void
{
Assert::assertDirectoryIsReadable(...\func_get_args());
}
/**
* Asserts that a directory exists and is not readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertDirectoryNotIsReadable
*/
function assertDirectoryNotIsReadable(string $directory, string $message = ''): void
{
Assert::assertDirectoryNotIsReadable(...\func_get_args());
}
/**
* Asserts that a directory exists and is writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertDirectoryIsWritable
*/
function assertDirectoryIsWritable(string $directory, string $message = ''): void
{
Assert::assertDirectoryIsWritable(...\func_get_args());
}
/**
* Asserts that a directory exists and is not writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertDirectoryNotIsWritable
*/
function assertDirectoryNotIsWritable(string $directory, string $message = ''): void
{
Assert::assertDirectoryNotIsWritable(...\func_get_args());
}
/**
* Asserts that a file exists.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFileExists
*/
function assertFileExists(string $filename, string $message = ''): void
{
Assert::assertFileExists(...\func_get_args());
}
/**
* Asserts that a file does not exist.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFileNotExists
*/
function assertFileNotExists(string $filename, string $message = ''): void
{
Assert::assertFileNotExists(...\func_get_args());
}
/**
* Asserts that a file exists and is readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFileIsReadable
*/
function assertFileIsReadable(string $file, string $message = ''): void
{
Assert::assertFileIsReadable(...\func_get_args());
}
/**
* Asserts that a file exists and is not readable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFileNotIsReadable
*/
function assertFileNotIsReadable(string $file, string $message = ''): void
{
Assert::assertFileNotIsReadable(...\func_get_args());
}
/**
* Asserts that a file exists and is writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFileIsWritable
*/
function assertFileIsWritable(string $file, string $message = ''): void
{
Assert::assertFileIsWritable(...\func_get_args());
}
/**
* Asserts that a file exists and is not writable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFileNotIsWritable
*/
function assertFileNotIsWritable(string $file, string $message = ''): void
{
Assert::assertFileNotIsWritable(...\func_get_args());
}
/**
* Asserts that a condition is true.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert true $condition
*
* @see Assert::assertTrue
*/
function assertTrue($condition, string $message = ''): void
{
Assert::assertTrue(...\func_get_args());
}
/**
* Asserts that a condition is not true.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !true $condition
*
* @see Assert::assertNotTrue
*/
function assertNotTrue($condition, string $message = ''): void
{
Assert::assertNotTrue(...\func_get_args());
}
/**
* Asserts that a condition is false.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert false $condition
*
* @see Assert::assertFalse
*/
function assertFalse($condition, string $message = ''): void
{
Assert::assertFalse(...\func_get_args());
}
/**
* Asserts that a condition is not false.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !false $condition
*
* @see Assert::assertNotFalse
*/
function assertNotFalse($condition, string $message = ''): void
{
Assert::assertNotFalse(...\func_get_args());
}
/**
* Asserts that a variable is null.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert null $actual
*
* @see Assert::assertNull
*/
function assertNull($actual, string $message = ''): void
{
Assert::assertNull(...\func_get_args());
}
/**
* Asserts that a variable is not null.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !null $actual
*
* @see Assert::assertNotNull
*/
function assertNotNull($actual, string $message = ''): void
{
Assert::assertNotNull(...\func_get_args());
}
/**
* Asserts that a variable is finite.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertFinite
*/
function assertFinite($actual, string $message = ''): void
{
Assert::assertFinite(...\func_get_args());
}
/**
* Asserts that a variable is infinite.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertInfinite
*/
function assertInfinite($actual, string $message = ''): void
{
Assert::assertInfinite(...\func_get_args());
}
/**
* Asserts that a variable is nan.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNan
*/
function assertNan($actual, string $message = ''): void
{
Assert::assertNan(...\func_get_args());
}
/**
* Asserts that a class has a specified attribute.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertClassHasAttribute
*/
function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void
{
Assert::assertClassHasAttribute(...\func_get_args());
}
/**
* Asserts that a class does not have a specified attribute.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertClassNotHasAttribute
*/
function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void
{
Assert::assertClassNotHasAttribute(...\func_get_args());
}
/**
* Asserts that a class has a specified static attribute.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertClassHasStaticAttribute
*/
function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
{
Assert::assertClassHasStaticAttribute(...\func_get_args());
}
/**
* Asserts that a class does not have a specified static attribute.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertClassNotHasStaticAttribute
*/
function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
{
Assert::assertClassNotHasStaticAttribute(...\func_get_args());
}
/**
* Asserts that an object has a specified attribute.
*
* @param object $object
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertObjectHasAttribute
*/
function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void
{
Assert::assertObjectHasAttribute(...\func_get_args());
}
/**
* Asserts that an object does not have a specified attribute.
*
* @param object $object
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertObjectNotHasAttribute
*/
function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void
{
Assert::assertObjectNotHasAttribute(...\func_get_args());
}
/**
* Asserts that two variables have the same type and value.
* Used on objects, it asserts that two variables reference
* the same object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-template ExpectedType
* @psalm-param ExpectedType $expected
* @psalm-assert =ExpectedType $actual
*
* @see Assert::assertSame
*/
function assertSame($expected, $actual, string $message = ''): void
{
Assert::assertSame(...\func_get_args());
}
/**
* Asserts that a variable and an attribute of an object have the same type
* and value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeSame
*/
function assertAttributeSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
Assert::assertAttributeSame(...\func_get_args());
}
/**
* Asserts that two variables do not have the same type and value.
* Used on objects, it asserts that two variables do not reference
* the same object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotSame
*/
function assertNotSame($expected, $actual, string $message = ''): void
{
Assert::assertNotSame(...\func_get_args());
}
/**
* Asserts that a variable and an attribute of an object do not have the
* same type and value.
*
* @param object|string $actualClassOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeNotSame
*/
function assertAttributeNotSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
{
Assert::assertAttributeNotSame(...\func_get_args());
}
/**
* Asserts that a variable is of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @psalm-template ExpectedType of object
* @psalm-param class-string<ExpectedType> $expected
* @psalm-assert ExpectedType $actual
*
* @see Assert::assertInstanceOf
*/
function assertInstanceOf(string $expected, $actual, string $message = ''): void
{
Assert::assertInstanceOf(...\func_get_args());
}
/**
* Asserts that an attribute is of a given type.
*
* @param object|string $classOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @psalm-param class-string $expected
*
* @see Assert::assertAttributeInstanceOf
*/
function assertAttributeInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void
{
Assert::assertAttributeInstanceOf(...\func_get_args());
}
/**
* Asserts that a variable is not of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @psalm-template ExpectedType of object
* @psalm-param class-string<ExpectedType> $expected
* @psalm-assert !ExpectedType $actual
*
* @see Assert::assertNotInstanceOf
*/
function assertNotInstanceOf(string $expected, $actual, string $message = ''): void
{
Assert::assertNotInstanceOf(...\func_get_args());
}
/**
* Asserts that an attribute is of a given type.
*
* @param object|string $classOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @psalm-param class-string $expected
*
* @see Assert::assertAttributeNotInstanceOf
*/
function assertAttributeNotInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void
{
Assert::assertAttributeNotInstanceOf(...\func_get_args());
}
/**
* Asserts that a variable is of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369
* @codeCoverageIgnore
*
* @see Assert::assertInternalType
*/
function assertInternalType(string $expected, $actual, string $message = ''): void
{
Assert::assertInternalType(...\func_get_args());
}
/**
* Asserts that an attribute is of a given type.
*
* @param object|string $classOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeInternalType
*/
function assertAttributeInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void
{
Assert::assertAttributeInternalType(...\func_get_args());
}
/**
* Asserts that a variable is of type array.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert array $actual
*
* @see Assert::assertIsArray
*/
function assertIsArray($actual, string $message = ''): void
{
Assert::assertIsArray(...\func_get_args());
}
/**
* Asserts that a variable is of type bool.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert bool $actual
*
* @see Assert::assertIsBool
*/
function assertIsBool($actual, string $message = ''): void
{
Assert::assertIsBool(...\func_get_args());
}
/**
* Asserts that a variable is of type float.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert float $actual
*
* @see Assert::assertIsFloat
*/
function assertIsFloat($actual, string $message = ''): void
{
Assert::assertIsFloat(...\func_get_args());
}
/**
* Asserts that a variable is of type int.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert int $actual
*
* @see Assert::assertIsInt
*/
function assertIsInt($actual, string $message = ''): void
{
Assert::assertIsInt(...\func_get_args());
}
/**
* Asserts that a variable is of type numeric.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert numeric $actual
*
* @see Assert::assertIsNumeric
*/
function assertIsNumeric($actual, string $message = ''): void
{
Assert::assertIsNumeric(...\func_get_args());
}
/**
* Asserts that a variable is of type object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert object $actual
*
* @see Assert::assertIsObject
*/
function assertIsObject($actual, string $message = ''): void
{
Assert::assertIsObject(...\func_get_args());
}
/**
* Asserts that a variable is of type resource.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert resource $actual
*
* @see Assert::assertIsResource
*/
function assertIsResource($actual, string $message = ''): void
{
Assert::assertIsResource(...\func_get_args());
}
/**
* Asserts that a variable is of type string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert string $actual
*
* @see Assert::assertIsString
*/
function assertIsString($actual, string $message = ''): void
{
Assert::assertIsString(...\func_get_args());
}
/**
* Asserts that a variable is of type scalar.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert scalar $actual
*
* @see Assert::assertIsScalar
*/
function assertIsScalar($actual, string $message = ''): void
{
Assert::assertIsScalar(...\func_get_args());
}
/**
* Asserts that a variable is of type callable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert callable $actual
*
* @see Assert::assertIsCallable
*/
function assertIsCallable($actual, string $message = ''): void
{
Assert::assertIsCallable(...\func_get_args());
}
/**
* Asserts that a variable is of type iterable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert iterable $actual
*
* @see Assert::assertIsIterable
*/
function assertIsIterable($actual, string $message = ''): void
{
Assert::assertIsIterable(...\func_get_args());
}
/**
* Asserts that a variable is not of a given type.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369
* @codeCoverageIgnore
*
* @see Assert::assertNotInternalType
*/
function assertNotInternalType(string $expected, $actual, string $message = ''): void
{
Assert::assertNotInternalType(...\func_get_args());
}
/**
* Asserts that a variable is not of type array.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !array $actual
*
* @see Assert::assertIsNotArray
*/
function assertIsNotArray($actual, string $message = ''): void
{
Assert::assertIsNotArray(...\func_get_args());
}
/**
* Asserts that a variable is not of type bool.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !bool $actual
*
* @see Assert::assertIsNotBool
*/
function assertIsNotBool($actual, string $message = ''): void
{
Assert::assertIsNotBool(...\func_get_args());
}
/**
* Asserts that a variable is not of type float.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !float $actual
*
* @see Assert::assertIsNotFloat
*/
function assertIsNotFloat($actual, string $message = ''): void
{
Assert::assertIsNotFloat(...\func_get_args());
}
/**
* Asserts that a variable is not of type int.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !int $actual
*
* @see Assert::assertIsNotInt
*/
function assertIsNotInt($actual, string $message = ''): void
{
Assert::assertIsNotInt(...\func_get_args());
}
/**
* Asserts that a variable is not of type numeric.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !numeric $actual
*
* @see Assert::assertIsNotNumeric
*/
function assertIsNotNumeric($actual, string $message = ''): void
{
Assert::assertIsNotNumeric(...\func_get_args());
}
/**
* Asserts that a variable is not of type object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !object $actual
*
* @see Assert::assertIsNotObject
*/
function assertIsNotObject($actual, string $message = ''): void
{
Assert::assertIsNotObject(...\func_get_args());
}
/**
* Asserts that a variable is not of type resource.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !resource $actual
*
* @see Assert::assertIsNotResource
*/
function assertIsNotResource($actual, string $message = ''): void
{
Assert::assertIsNotResource(...\func_get_args());
}
/**
* Asserts that a variable is not of type string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !string $actual
*
* @see Assert::assertIsNotString
*/
function assertIsNotString($actual, string $message = ''): void
{
Assert::assertIsNotString(...\func_get_args());
}
/**
* Asserts that a variable is not of type scalar.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !scalar $actual
*
* @see Assert::assertIsNotScalar
*/
function assertIsNotScalar($actual, string $message = ''): void
{
Assert::assertIsNotScalar(...\func_get_args());
}
/**
* Asserts that a variable is not of type callable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !callable $actual
*
* @see Assert::assertIsNotCallable
*/
function assertIsNotCallable($actual, string $message = ''): void
{
Assert::assertIsNotCallable(...\func_get_args());
}
/**
* Asserts that a variable is not of type iterable.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !iterable $actual
*
* @see Assert::assertIsNotIterable
*/
function assertIsNotIterable($actual, string $message = ''): void
{
Assert::assertIsNotIterable(...\func_get_args());
}
/**
* Asserts that an attribute is of a given type.
*
* @param object|string $classOrObject
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
* @codeCoverageIgnore
*
* @see Assert::assertAttributeNotInternalType
*/
function assertAttributeNotInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void
{
Assert::assertAttributeNotInternalType(...\func_get_args());
}
/**
* Asserts that a string matches a given regular expression.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertRegExp
*/
function assertRegExp(string $pattern, string $string, string $message = ''): void
{
Assert::assertRegExp(...\func_get_args());
}
/**
* Asserts that a string does not match a given regular expression.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertNotRegExp
*/
function assertNotRegExp(string $pattern, string $string, string $message = ''): void
{
Assert::assertNotRegExp(...\func_get_args());
}
/**
* Assert that the size of two arrays (or `Countable` or `Traversable` objects)
* is the same.
*
* @param Countable|iterable $expected
* @param Countable|iterable $actual
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertSameSize
*/
function assertSameSize($expected, $actual, string $message = ''): void
{
Assert::assertSameSize(...\func_get_args());
}
/**
* Assert that the size of two arrays (or `Countable` or `Traversable` objects)
* is not the same.
*
* @param Countable|iterable $expected
* @param Countable|iterable $actual
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertNotSameSize
*/
function assertNotSameSize($expected, $actual, string $message = ''): void
{
Assert::assertNotSameSize(...\func_get_args());
}
/**
* Asserts that a string matches a given format string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringMatchesFormat
*/
function assertStringMatchesFormat(string $format, string $string, string $message = ''): void
{
Assert::assertStringMatchesFormat(...\func_get_args());
}
/**
* Asserts that a string does not match a given format string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringNotMatchesFormat
*/
function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void
{
Assert::assertStringNotMatchesFormat(...\func_get_args());
}
/**
* Asserts that a string matches a given format file.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringMatchesFormatFile
*/
function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
{
Assert::assertStringMatchesFormatFile(...\func_get_args());
}
/**
* Asserts that a string does not match a given format string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringNotMatchesFormatFile
*/
function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
{
Assert::assertStringNotMatchesFormatFile(...\func_get_args());
}
/**
* Asserts that a string starts with a given prefix.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringStartsWith
*/
function assertStringStartsWith(string $prefix, string $string, string $message = ''): void
{
Assert::assertStringStartsWith(...\func_get_args());
}
/**
* Asserts that a string starts not with a given prefix.
*
* @param string $prefix
* @param string $string
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringStartsNotWith
*/
function assertStringStartsNotWith($prefix, $string, string $message = ''): void
{
Assert::assertStringStartsNotWith(...\func_get_args());
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringContainsString
*/
function assertStringContainsString(string $needle, string $haystack, string $message = ''): void
{
Assert::assertStringContainsString(...\func_get_args());
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringContainsStringIgnoringCase
*/
function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
{
Assert::assertStringContainsStringIgnoringCase(...\func_get_args());
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringNotContainsString
*/
function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void
{
Assert::assertStringNotContainsString(...\func_get_args());
}
/**
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringNotContainsStringIgnoringCase
*/
function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
{
Assert::assertStringNotContainsStringIgnoringCase(...\func_get_args());
}
/**
* Asserts that a string ends with a given suffix.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringEndsWith
*/
function assertStringEndsWith(string $suffix, string $string, string $message = ''): void
{
Assert::assertStringEndsWith(...\func_get_args());
}
/**
* Asserts that a string ends not with a given suffix.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertStringEndsNotWith
*/
function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void
{
Assert::assertStringEndsNotWith(...\func_get_args());
}
/**
* Asserts that two XML files are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertXmlFileEqualsXmlFile
*/
function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
{
Assert::assertXmlFileEqualsXmlFile(...\func_get_args());
}
/**
* Asserts that two XML files are not equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertXmlFileNotEqualsXmlFile
*/
function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
{
Assert::assertXmlFileNotEqualsXmlFile(...\func_get_args());
}
/**
* Asserts that two XML documents are equal.
*
* @param DOMDocument|string $actualXml
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertXmlStringEqualsXmlFile
*/
function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
{
Assert::assertXmlStringEqualsXmlFile(...\func_get_args());
}
/**
* Asserts that two XML documents are not equal.
*
* @param DOMDocument|string $actualXml
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertXmlStringNotEqualsXmlFile
*/
function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
{
Assert::assertXmlStringNotEqualsXmlFile(...\func_get_args());
}
/**
* Asserts that two XML documents are equal.
*
* @param DOMDocument|string $expectedXml
* @param DOMDocument|string $actualXml
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertXmlStringEqualsXmlString
*/
function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
{
Assert::assertXmlStringEqualsXmlString(...\func_get_args());
}
/**
* Asserts that two XML documents are not equal.
*
* @param DOMDocument|string $expectedXml
* @param DOMDocument|string $actualXml
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
* @throws Exception
*
* @see Assert::assertXmlStringNotEqualsXmlString
*/
function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
{
Assert::assertXmlStringNotEqualsXmlString(...\func_get_args());
}
/**
* Asserts that a hierarchy of DOMElements matches.
*
* @throws AssertionFailedError
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertEqualXMLStructure
*/
function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void
{
Assert::assertEqualXMLStructure(...\func_get_args());
}
/**
* Evaluates a PHPUnit\Framework\Constraint matcher object.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertThat
*/
function assertThat($value, Constraint $constraint, string $message = ''): void
{
Assert::assertThat(...\func_get_args());
}
/**
* Asserts that a string is a valid JSON string.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertJson
*/
function assertJson(string $actualJson, string $message = ''): void
{
Assert::assertJson(...\func_get_args());
}
/**
* Asserts that two given JSON encoded objects or arrays are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertJsonStringEqualsJsonString
*/
function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void
{
Assert::assertJsonStringEqualsJsonString(...\func_get_args());
}
/**
* Asserts that two given JSON encoded objects or arrays are not equal.
*
* @param string $expectedJson
* @param string $actualJson
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertJsonStringNotEqualsJsonString
*/
function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void
{
Assert::assertJsonStringNotEqualsJsonString(...\func_get_args());
}
/**
* Asserts that the generated JSON encoded object and the content of the given file are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertJsonStringEqualsJsonFile
*/
function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
{
Assert::assertJsonStringEqualsJsonFile(...\func_get_args());
}
/**
* Asserts that the generated JSON encoded object and the content of the given file are not equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertJsonStringNotEqualsJsonFile
*/
function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
{
Assert::assertJsonStringNotEqualsJsonFile(...\func_get_args());
}
/**
* Asserts that two JSON files are equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertJsonFileEqualsJsonFile
*/
function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
{
Assert::assertJsonFileEqualsJsonFile(...\func_get_args());
}
/**
* Asserts that two JSON files are not equal.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @see Assert::assertJsonFileNotEqualsJsonFile
*/
function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
{
Assert::assertJsonFileNotEqualsJsonFile(...\func_get_args());
}
function logicalAnd(): LogicalAnd
{
return Assert::logicalAnd(...\func_get_args());
}
function logicalOr(): LogicalOr
{
return Assert::logicalOr(...\func_get_args());
}
function logicalNot(Constraint $constraint): LogicalNot
{
return Assert::logicalNot(...\func_get_args());
}
function logicalXor(): LogicalXor
{
return Assert::logicalXor(...\func_get_args());
}
function anything(): IsAnything
{
return Assert::anything(...\func_get_args());
}
function isTrue(): IsTrue
{
return Assert::isTrue(...\func_get_args());
}
function callback(callable $callback): Callback
{
return Assert::callback(...\func_get_args());
}
function isFalse(): IsFalse
{
return Assert::isFalse(...\func_get_args());
}
function isJson(): IsJson
{
return Assert::isJson(...\func_get_args());
}
function isNull(): IsNull
{
return Assert::isNull(...\func_get_args());
}
function isFinite(): IsFinite
{
return Assert::isFinite(...\func_get_args());
}
function isInfinite(): IsInfinite
{
return Assert::isInfinite(...\func_get_args());
}
function isNan(): IsNan
{
return Assert::isNan(...\func_get_args());
}
function attribute(Constraint $constraint, string $attributeName): Attribute
{
return Assert::attribute(...\func_get_args());
}
function contains($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): TraversableContains
{
return Assert::contains(...\func_get_args());
}
function containsOnly(string $type): TraversableContainsOnly
{
return Assert::containsOnly(...\func_get_args());
}
function containsOnlyInstancesOf(string $className): TraversableContainsOnly
{
return Assert::containsOnlyInstancesOf(...\func_get_args());
}
function arrayHasKey($key): ArrayHasKey
{
return Assert::arrayHasKey(...\func_get_args());
}
function equalTo($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): IsEqual
{
return Assert::equalTo(...\func_get_args());
}
function attributeEqualTo(string $attributeName, $value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): Attribute
{
return Assert::attributeEqualTo(...\func_get_args());
}
function isEmpty(): IsEmpty
{
return Assert::isEmpty(...\func_get_args());
}
function isWritable(): IsWritable
{
return Assert::isWritable(...\func_get_args());
}
function isReadable(): IsReadable
{
return Assert::isReadable(...\func_get_args());
}
function directoryExists(): DirectoryExists
{
return Assert::directoryExists(...\func_get_args());
}
function fileExists(): FileExists
{
return Assert::fileExists(...\func_get_args());
}
function greaterThan($value): GreaterThan
{
return Assert::greaterThan(...\func_get_args());
}
function greaterThanOrEqual($value): LogicalOr
{
return Assert::greaterThanOrEqual(...\func_get_args());
}
function classHasAttribute(string $attributeName): ClassHasAttribute
{
return Assert::classHasAttribute(...\func_get_args());
}
function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute
{
return Assert::classHasStaticAttribute(...\func_get_args());
}
function objectHasAttribute($attributeName): ObjectHasAttribute
{
return Assert::objectHasAttribute(...\func_get_args());
}
function identicalTo($value): IsIdentical
{
return Assert::identicalTo(...\func_get_args());
}
function isInstanceOf(string $className): IsInstanceOf
{
return Assert::isInstanceOf(...\func_get_args());
}
function isType(string $type): IsType
{
return Assert::isType(...\func_get_args());
}
function lessThan($value): LessThan
{
return Assert::lessThan(...\func_get_args());
}
function lessThanOrEqual($value): LogicalOr
{
return Assert::lessThanOrEqual(...\func_get_args());
}
function matchesRegularExpression(string $pattern): RegularExpression
{
return Assert::matchesRegularExpression(...\func_get_args());
}
function matches(string $string): StringMatchesFormatDescription
{
return Assert::matches(...\func_get_args());
}
function stringStartsWith($prefix): StringStartsWith
{
return Assert::stringStartsWith(...\func_get_args());
}
function stringContains(string $string, bool $case = true): StringContains
{
return Assert::stringContains(...\func_get_args());
}
function stringEndsWith(string $suffix): StringEndsWith
{
return Assert::stringEndsWith(...\func_get_args());
}
function countOf(int $count): Count
{
return Assert::countOf(...\func_get_args());
}
/**
* Returns a matcher that matches when the method is executed
* zero or more times.
*/
function any(): AnyInvokedCountMatcher
{
return new AnyInvokedCountMatcher;
}
/**
* Returns a matcher that matches when the method is never executed.
*/
function never(): InvokedCountMatcher
{
return new InvokedCountMatcher(0);
}
/**
* Returns a matcher that matches when the method is executed
* at least N times.
*/
function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher
{
return new InvokedAtLeastCountMatcher(
$requiredInvocations
);
}
/**
* Returns a matcher that matches when the method is executed at least once.
*/
function atLeastOnce(): InvokedAtLeastOnceMatcher
{
return new InvokedAtLeastOnceMatcher;
}
/**
* Returns a matcher that matches when the method is executed exactly once.
*/
function once(): InvokedCountMatcher
{
return new InvokedCountMatcher(1);
}
/**
* Returns a matcher that matches when the method is executed
* exactly $count times.
*/
function exactly(int $count): InvokedCountMatcher
{
return new InvokedCountMatcher($count);
}
/**
* Returns a matcher that matches when the method is executed
* at most N times.
*/
function atMost(int $allowedInvocations): InvokedAtMostCountMatcher
{
return new InvokedAtMostCountMatcher($allowedInvocations);
}
/**
* Returns a matcher that matches when the method is executed
* at the given index.
*/
function at(int $index): InvokedAtIndexMatcher
{
return new InvokedAtIndexMatcher($index);
}
function returnValue($value): ReturnStub
{
return new ReturnStub($value);
}
function returnValueMap(array $valueMap): ReturnValueMapStub
{
return new ReturnValueMapStub($valueMap);
}
function returnArgument(int $argumentIndex): ReturnArgumentStub
{
return new ReturnArgumentStub($argumentIndex);
}
function returnCallback($callback): ReturnCallbackStub
{
return new ReturnCallbackStub($callback);
}
/**
* Returns the current object.
*
* This method is useful when mocking a fluent interface.
*/
function returnSelf(): ReturnSelfStub
{
return new ReturnSelfStub;
}
function throwException(Throwable $exception): ExceptionStub
{
return new ExceptionStub($exception);
}
function onConsecutiveCalls(): ConsecutiveCallsStub
{
$args = \func_get_args();
return new ConsecutiveCallsStub($args);
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Error;
use PHPUnit\Framework\Exception;
class Error extends Exception
{
public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->file = $file;
$this->line = $line;
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Error;
final class Deprecated extends Error
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Error;
final class Notice extends Error
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Error;
final class Warning extends Error
{
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class WarningTestCase extends TestCase
{
/**
* @var bool
*/
protected $backupGlobals = false;
/**
* @var bool
*/
protected $backupStaticAttributes = false;
/**
* @var bool
*/
protected $runTestInSeparateProcess = false;
/**
* @var bool
*/
protected $useErrorHandler = false;
/**
* @var string
*/
private $message;
/**
* @param string $message
*/
public function __construct($message = '')
{
$this->message = $message;
parent::__construct('Warning');
}
public function getMessage(): string
{
return $this->message;
}
/**
* Returns a string representation of the test case.
*/
public function toString(): string
{
return 'Warning';
}
/**
* @throws Exception
*/
protected function runTest(): void
{
throw new Warning($this->message);
}
}
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class InvalidParameterGroupException extends Exception
{
}
<?php
namespace PHPSTORM_META {
override(
\PHPUnit\Framework\TestCase::createMock(0),
map([
'@&\PHPUnit\Framework\MockObject\MockObject',
])
);
override(
\PHPUnit\Framework\TestCase::createStub(0),
map([
'@&\PHPUnit\Framework\MockObject\Stub',
])
);
override(
\PHPUnit\Framework\TestCase::createConfiguredMock(0),
map([
'@&\PHPUnit\Framework\MockObject\MockObject',
])
);
override(
\PHPUnit\Framework\TestCase::createPartialMock(0),
map([
'@&\PHPUnit\Framework\MockObject\MockObject',
])
);
override(
\PHPUnit\Framework\TestCase::createTestProxy(0),
map([
'@&\PHPUnit\Framework\MockObject\MockObject',
])
);
override(
\PHPUnit\Framework\TestCase::getMockForAbstractClass(0),
map([
'@&\PHPUnit\Framework\MockObject\MockObject',
])
);
}
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDOMXPath extension to PHP's DOMXPath.
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMXPath extends \DOMXPath {
/**
* @var \DOMDocument
*/
protected $doc;
/**
* @param \DOMDocument $doc
*/
public function __construct(\DOMDocument $doc) {
parent::__construct($doc);
$this->doc = $doc;
}
/**
* @param string $xpath
* @param array $valueMap
*
* @return string
*/
public function prepare($xpath, array $valueMap) {
if (count($valueMap)==0) {
return $xpath;
}
foreach($valueMap as $key => $value) {
$xpath = str_replace(':'.$key, $this->quote($value), $xpath);
}
return $xpath;
}
/**
* @param string $q
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return \DOMNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
libxml_clear_errors();
if (version_compare(PHP_VERSION, '5.3.3', '<') || strpos(PHP_VERSION, 'hiphop') || strpos(PHP_VERSION, 'hhvm')) {
$rc = parent::query($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement);
} else {
$rc = parent::query($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement, $registerNodeNS);
}
if (libxml_get_last_error()) {
throw new fDOMException('evaluating xpath expression failed.', fDOMException::QueryError);
}
return $rc;
}
/**
* @param string $q
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return mixed
*/
public function evaluate($q, \DOMNode $ctx = null, $registerNodeNS = true) {
libxml_clear_errors();
if (version_compare(PHP_VERSION, '5.3.3', '<') || strpos(PHP_VERSION, 'hiphop') || strpos(PHP_VERSION, 'hhvm')) {
$rc = parent::evaluate($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement);
} else {
$rc = parent::evaluate($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement, $registerNodeNS);
}
if (libxml_get_last_error()) {
throw new fDOMException('evaluating xpath expression failed.', fDOMException::QueryError);
}
return $rc;
}
/**
* @param string $q
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return \DOMNode|mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
$rc = $this->evaluate($q, $ctx, $registerNodeNS);
if ($rc instanceof \DOMNodelist) {
return $rc->item(0);
}
return $rc;
}
/**
* @param string $str
*
* @return string
*/
public function quote($str) {
if (strpos($str, '"') === false) {
return '"'.$str.'"';
}
$parts = explode('"', $str);
return 'concat("' . join('",\'"\',"', $parts).'")';
}
}
}
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDomElement
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
* @property fDOMDocument $ownerDocument
*
*/
class fDOMElement extends \DOMElement {
/**
* @return string
*/
public function __toString() {
return $this->C14N();
}
/**
* Forward to fDomDocument->query()
*
* @param string $q XPath to use
* @param \DOMNode $ctx \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return \DomNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->query($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->queryOne()
*
* @param string $q XPath to use
* @param \DOMNode $ctx (optional) \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->queryOne($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->select()
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->select($selector, $ctx, $registerNodeNS);
}
/**
* Parse and append XML String to node
*
* @param String $str string to process
*
* @return fDomDocumentFragment Reference to the created Fragment
*/
public function appendXML($str) {
$frag = $this->ownerDocument->createDocumentFragment();
$frag->appendXML($str);
$this->appendChild($frag);
return $frag;
}
/**
* Create a new element and append it
*
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElement($name, $content = null, $asTextnode = FALSE) {
$node = $this->ownerDocument->createElement($name, $content, $asTextnode);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $ns Namespace of node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementNS($ns, $name, $content = null, $asTextnode = FALSE) {
$node = $this->ownerDocument->createElementNS($ns, $name, $content, $asTextnode);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $prefix Namespace prefix for node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementPrefix($prefix, $name, $content = null, $asTextnode = FALSE) {
$node = $this->ownerDocument->createElementPrefix($prefix, $name, $content, $asTextnode);
$this->appendChild($node);
return $node;
}
/**
* Create a new text node and append it
*
* @param string $content Text content to be added
*
* @return \DOMText
*/
public function appendTextNode($content) {
$text = $this->ownerDocument->createTextNode($content);
$this->appendChild($text);
return $text;
}
/**
* Create a new fDOMElement
*
* @see fDOMDocument::createElement
*
* @param string $name
* @param string $content
* @param bool $asTextnode
*
* @return fDOMElement
*/
public function createElement($name, $content = NULL, $asTextnode = FALSE) {
return $this->ownerDocument->createElement($name, $content, $asTextnode);
}
/**
* Create a new fDOMElement in namespace defined by prefix
*
* @see fDOMDocument::createElementPrefix
*
* @param string $prefix
* @param string $name
* @param string $content
* @param bool $asTextNode
*
* @return fDOMElement
*/
public function createElementPrefix($prefix, $name, $content = NULL, $asTextNode = FALSE) {
return $this->ownerDocument->createElementPrefix($prefix, $name, $content, $asTextNode);
}
/**
* Create a new fDOMElement within given namespace and return it
*
* @param string $namespace Namespace URI for node to create
* @param string $name Name of node to create
* @param null $content Content to set (optional)
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement
*/
public function createElementNS($namespace, $name, $content = NULL, $asTextNode = FALSE) {
return $this->ownerDocument->createElementNS($namespace, $name, $content, $asTextNode);
}
/**
* Wrapper to DomElement->getAttribute with default value option
*
* Note: A set but emptry attribute does NOT trigger use of the default
*
* @param string $attr Attribute to access
* @param string $default Default value to use if the attribute is not set
*
* @return string
*/
public function getAttribute($attr, $default='') {
return $this->hasAttribute($attr) ? parent::getAttribute($attr) : $default;
}
/**
* Wrapper to DomElement->getAttributeNS with default value option
*
* Note: A set but empty attribute does NOT trigger use of the default
*
* @param string $ns Namespace of attribute
* @param string $attr Attribute to access
* @param string $default Default value to use if the attribute is not set
*
* @return string
*/
public function getAttributeNS($ns, $attr, $default='') {
return $this->hasAttributeNS($ns, $attr) ? parent::getAttributeNS($ns, $attr) : $default;
}
/**
* Wrapper to DOMElement::setAttribute with additional entities support
*
* @param string $attr Attribute name to set
* @param string $value Value to set attribute to
* @param bool $keepEntities Flag to signale if entities should be kept
*
* @throws fDOMException
*
* @return \DOMAttr
*
* @see DOMElement::setAttribute()
*/
public function setAttribute($attr, $value, $keepEntities=false) {
if ($keepEntities === true) {
$attrNode = $this->ownerDocument->createAttribute($attr);
if (!$attrNode) {
throw new fDOMException("Setting attribute '$attr' failed.", fDOMException::SetFailedError);
}
$attrNode->value = $value;
$this->appendChild($attrNode);
return $attrNode;
}
return parent::setAttribute($attr, $value);
}
/**
* Wrapper to namespace aware DOMElement::setAttributeNS with additional entities support
*
* @param string $ns namespace attribute should be in
* @param string $attr Attribute name to set
* @param string $value Value to set attribute to
* @param bool $keepEntities Flag to signale if entities should be kept
*
* @throws fDOMException
*
* @return \DOMAttr|null
* @see DOMElement::setAttribute()
*/
public function setAttributeNS($ns, $attr, $value, $keepEntities=false) {
if ($keepEntities === true) {
$attrNode = $this->ownerDocument->createAttributeNS($ns, $attr);
if (!$attrNode) {
throw new fDOMException("Setting attribute '$attr' failed.", fDOMException::SetFailedError);
}
$attrNode->value = $value;
$this->appendChild($attrNode);
return $attrNode;
}
return parent::setAttributeNS($ns, $attr, $value);
}
/**
* Helper to add multiple attributes to an element
*
* @param array $attr Attributes to add as key-value pair
* @param bool $keepEntities Flag wether to keep entities
*
* @return array List with references to created DOMAttr
*/
public function setAttributes(array $attr, $keepEntities=false) {
$attList = array();
foreach($attr as $name => $value) {
$attList[] = $this->setAttribute($name, $value, $keepEntities);
}
return $attList;
}
/**
* Helper to add multiple attributes with the given namespace and prefix
*
* @param string $ns Namespace of attribute
* @param string $prefix Namespace prefix for attribute to create
* @param array $attr Attributes to add
* @param bool $keepEntities Flag wether to keep entities
*
* @return void
*/
public function setAttributesNS($ns, $prefix, array $attr, $keepEntities=false) {
foreach($attr as $name => $value) {
$this->setAttributeNS($ns, $prefix.':'.$name, $value, $keepEntities);
}
}
/**
* Helper method to get children by name
*
* @param string $tagName tagname to search for
*
* @return \DOMNodeList
*/
public function getChildrenByTagName($tagName) {
return $this->query("*[local-name()='$tagName']");
}
/**
* Helper method to get children by name and namespace
*
* @param string $ns namespace nodes have to be in
* @param string $tagName tagname to search for
*
* @return \DOMNodeList
*/
public function getChildrenByTagNameNS($ns, $tagName) {
return $this->query("*[local-name()='$tagName' and namespace-uri()='$ns']");
}
/**
* Check if the given node is in the same document
*
* @param \DomNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DomNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
/**
* Wrapper to DomDocument::saveXML() with current node as context
*
* @return string
*/
public function saveXML() {
return $this->ownerDocument->saveXML($this);
}
/**
* Wrapper to DomDocument::saveHTML() with current node as context
*
* @return string
*/
public function saveHTML() {
return $this->ownerDocument->saveHTML($this);
}
} // fDOMElement
}
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license BSD License
*/
namespace TheSeer\fDOM {
/**
* fDOMException
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMException extends \Exception {
const LoadError = 1;
const ParseError = 2;
const SaveError = 3;
const QueryError = 4;
const RegistrationFailed = 5;
const NoDOMXPath = 6;
const UnboundPrefix = 7;
const SetFailedError = 8;
const NameInvalid = 9;
/**
* List of libxml error objects
*
* @var array
*/
private $errorList;
/**
* Full Error message
*
* @var string
*/
private $fullMessage = null;
/**
* Short Error Message
*
* @var string
*/
private $shortMessage = null;
private static $fullMesageMode = true;
/**
* Constructor
*
* @param string $message Exception message
* @param integer $code Exception code
* @param \Exception $chain optional chained exception
*
*/
public function __construct($message, $code = 0, \Exception $chain = NULL) {
$this->shortMessage = $message;
$this->errorList = libxml_get_errors();
libxml_clear_errors();
parent :: __construct($message, $code, $chain);
$this->fullMessage = $message."\n\n";
foreach ($this->errorList as $error) {
// hack, skip "attempt to load external pseudo error"
if ($error->code=='1543') {
continue;
}
if (empty($error->file)) {
$this->fullMessage .= '[XML-STRING] ';
} else {
$this->fullMessage .= '['.$error->file.'] ';
}
$this->fullMessage .= '[Line: '.$error->line.' - Column: '.$error->column.'] ';
switch ($error->level) {
case LIBXML_ERR_WARNING:
$this->fullMessage .= "Warning $error->code: ";
break;
case LIBXML_ERR_ERROR:
$this->fullMessage .= "Error $error->code: ";
break;
case LIBXML_ERR_FATAL:
$this->fullMessage .= "Fatal Error $error->code: ";
break;
}
$this->fullMessage .= str_replace("\n", '', $error->message)."\n";
if (self::$fullMesageMode) {
$this->message = $this->fullMessage;
}
}
}
/**
* Accessor to fullMessage
*
* @return string
*/
public function getFullMessage() {
return $this->fullMessage;
}
/**
* Access to shortMessage
*
* @return string
*/
public function getShortMessage() {
return $this->shortMessage;
}
/**
* Accessor to errorList objets
*
* @return array
*/
public function getErrorList() {
return $this->errorList;
}
/**
* Toggle wehter getMessage() should return full or only exception message
*
* @param boolean $full Flag to enable or disable full message output
*
* @return void
*/
public function toggleFullMessage($full = true) {
$this->message = $full ? $this->fullMessage : $this->shortMessage;
}
/**
* Magic method for string context
*
* @return string
*/
public function __toString() {
return $this->fullMessage;
}
} // fDOMException
}
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
class XPathQueryException extends \Exception {
const KeyNotFound = 1;
}
}
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* Class XPathQuery
*
* @package TheSeer\fDOM
*/
class XPathQuery {
/**
* @var string
*/
private $query;
/**
* Key-value Map for bound values
*
* @var array
*/
private $values = array();
/**
* @param string $query
*/
public function __construct($query) {
$this->setQuery($query);
}
/**
* Set Query.
*
* @param string $query
*/
private function setQuery($query) {
$this->query = $query;
$res = preg_match_all('/(:(\w*))/', $query, $matches);
if ($res > 0) {
$this->values = array_fill_keys($matches[2], '');
}
}
/**
* Returns keys.
*
* @return array
*/
public function getKeys() {
return array_keys($this->values);
}
/**
* Bind value to key.
*
* @param string $key
* @param string $value
*
* @throws XPathQueryException
*/
public function bind($key, $value) {
if (!array_key_exists($key, $this->values)) {
throw new XPathQueryException("'$key' not found in query'", XPathQueryException::KeyNotFound );
}
$this->values[$key] = $value;
}
/**
* Generate query.
*
* @param \DOMNode $ctx
* @param array $values
*
* @return string
*/
public function generate(\DOMNode $ctx, array $values = NULL) {
return $this->buildQuery($this->getXPathObjectFor($ctx), $values);
}
/**
* Evaluate Query.
*
* @param \DOMNode $ctx
* @param array $values
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return mixed
*/
public function evaluate(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->evaluate($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
/**
* Execute Query.
*
* @param \DOMNode $ctx
* @param array $values
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return mixed
*/
public function query(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->evaluate($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
/**
* Execute Query and return first result.
*
* @param \DOMNode $ctx
* @param array $values
* @param bool $registerNodeNS
*
* @return \DOMNode
*/
public function queryOne(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->queryOne($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
/**
* Return xPath for node
*
* @param \DOMNode $ctx
*
* @throws fDOMException
*
* @return fDOMXPath
*/
private function getXPathObjectFor(\DOMNode $ctx) {
$dom = $ctx instanceof \DOMDocument ? $ctx : $ctx->ownerDocument;
if ($dom instanceOf fDOMDocument) {
return $dom->getDOMXPath();
}
return new fDOMXPath($dom);
}
/**
* Build query using values.
*
* @param fDOMXPath $xp
* @param array $values
*
* @throws XPathQueryException
*
* @return string
*/
private function buildQuery(fDOMXPath $xp, array $values = NULL) {
$backup = $this->values;
if (is_array($values) && count($values) > 0) {
foreach($values as $k => $v) {
$this->bind($k, $v);
}
}
$query = $xp->prepare($this->query, $this->values);
$this->values = $backup;
return $query;
}
}
}
<?php
// @codingStandardsIgnoreFile
// @codeCoverageIgnoreStart
// this is an autogenerated file - do not edit
spl_autoload_register(
function($class) {
static $classes = null;
if ($classes === null) {
$classes = array(
'theseer\\fdom\\css\\dollarequalrule' => '/css/DollarEqualRule.php',
'theseer\\fdom\\css\\notrule' => '/css/NotRule.php',
'theseer\\fdom\\css\\nthchildrule' => '/css/NthChildRule.php',
'theseer\\fdom\\css\\regexrule' => '/css/RegexRule.php',
'theseer\\fdom\\css\\ruleinterface' => '/css/RuleInterface.php',
'theseer\\fdom\\css\\translator' => '/css/Translator.php',
'theseer\\fdom\\fdomdocument' => '/fDOMDocument.php',
'theseer\\fdom\\fdomdocumentfragment' => '/fDOMDocumentFragment.php',
'theseer\\fdom\\fdomelement' => '/fDOMElement.php',
'theseer\\fdom\\fdomexception' => '/fDOMException.php',
'theseer\\fdom\\fdomnode' => '/fDOMNode.php',
'theseer\\fdom\\fdomxpath' => '/fDOMXPath.php',
'theseer\\fdom\\xpathquery' => '/XPathQuery.php',
'theseer\\fdom\\xpathqueryexception' => '/XPathQueryException.php'
);
}
$cn = strtolower($class);
if (isset($classes[$cn])) {
require __DIR__ . $classes[$cn];
}
}
);
// @codeCoverageIgnoreEnd
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDOMDocumentFragment
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
* @property fDOMDocument $ownerDocument
*
*/
class fDOMDocumentFragment extends \DOMDocumentFragment {
/**
* @return string
*/
public function __toString() {
return $this->ownerDocument->saveXML($this);
}
/**
* Wrapper to standard method with exception support
*
* @param string $str Data string to parse and append
*
* @throws fDOMException
*
* @return bool true on success
*/
public function appendXML($str) {
if (!parent::appendXML($str)) {
throw new fDOMException('Appending xml string failed', fDOMException::ParseError);
}
return true;
}
/**
* Create a new element and append it
*
* @param string $name Name of not element to create
* @param string $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElement($name, $content = null) {
$node = $this->ownerDocument->createElement($name, $content);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $ns Namespace of node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementNS($ns, $name, $content = null) {
$node = $this->ownerDocument->createElementNS($ns, $name, $content);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $prefix Namespace prefix for node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementPrefix($prefix, $name, $content = null, $asTextnode = FALSE) {
$node = $this->ownerDocument->createElementPrefix($prefix, $name, $content, $asTextnode);
$this->appendChild($node);
return $node;
}
/**
* Create a new text node and append it
*
* @param string $content Text content to be added
*
* @return \DOMText
*/
public function appendTextNode($content) {
$text = $this->ownerDocument->createTextNode($content);
$this->appendChild($text);
return $text;
}
/**
* Check if the given node is in the same document
*
* @param \DOMNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
/**
* Forward to fDomDocument->query()
*
* @param string $q XPath to use
* @param \DOMNode $ctx \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return \DomNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->query($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->queryOne()
*
* @param string $q XPath to use
* @param \DOMNode $ctx (optional) \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->queryOne($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->select()
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->select($selector, $ctx ? $ctx : $this, $registerNodeNS);
}
} // fDOMDocumentFragment
}
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDomNode
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
* @property fDOMDocument $ownerDocument
*
*/
class fDOMNode extends \DOMNode {
/**
* @return string
*/
public function __toString() {
return $this->C14N();
}
/**
* Create a new fDOMElement
*
* @see fDOMDocument::createElement
*
* @param string $name
* @param string $content
* @param bool $asTextnode
*
* @return fDOMElement
*/
public function createElement($name, $content = NULL, $asTextnode = FALSE) {
return $this->ownerDocument->createElement($name, $content, $asTextnode);
}
/**
* Create a new fDOMElement in namespace defined by prefix
*
* @see fDOMDocument::createElementPrefix
*
* @param string $prefix
* @param string $name
* @param string $content
* @param bool $asTextNode
*
* @return fDOMElement
*/
public function createElementPrefix($prefix, $name, $content = NULL, $asTextNode = FALSE) {
return $this->ownerDocument->createElementPrefix($prefix, $name, $content, $asTextNode);
}
/**
* Create a new fDOMElement within given namespace and return it
*
* @param string $namespace Namespace URI for node to create
* @param string $name Name of node to create
* @param null $content Content to set (optional)
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement
*/
public function createElementNS($namespace, $name, $content = NULL, $asTextNode = FALSE) {
return $this->ownerDocument->createElementNS($namespace, $name, $content, $asTextNode);
}
/**
* Forward to fDomDocument->query()
*
* @param string $q XPath to use
* @param \DOMNode $ctx \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return \DomNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->query($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->queryOne()
*
* @param string $q XPath to use
* @param \DOMNode $ctx (optional) \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->queryOne($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->select()
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->select($selector, $ctx, $registerNodeNS);
}
/**
* Check if the given node is in the same document
*
* @param \DomNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
/**
* Wrapper to DomDocument::saveXML() with current node as context
*
* @return string
*/
public function saveXML() {
return $this->ownerDocument->saveXML($this);
}
/**
* Wrapper to DomDocument::saveHTML() with current node as context
*
* @return string
*/
public function saveHTML() {
return $this->ownerDocument->saveHTML($this);
}
} // fDOMNode
}
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
use TheSeer\fDOM\CSS\Translator;
/**
* fDOMDocument extension to PHP's DOMDocument.
* This class adds various convenience methods to simplify APIs
* It is set to final since further extending it would even more
* break the Object structure after use of registerNodeClass.
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
* @property fDOMDocument $ownerDocument
*
*/
class fDOMDocument extends \DOMDocument {
/**
* XPath Object instance
*
* @var fDOMXPath
*/
private $xp = NULL;
/**
* List of registered prefixes and their namespace uri
* @var array
*/
private $prefixes = array();
/**
* Extended DOMDocument constructor
*
* @param string $version XML Version, should be 1.0
* @param string $encoding Encoding, defaults to utf-8
* @param array $streamOptions optional stream options array
*
* @return fDOMDocument
*/
public function __construct($version = '1.0', $encoding = 'utf-8', $streamOptions = NULL) {
if (!is_null($streamOptions)) {
$this->setStreamContext($streamOptions);
}
libxml_use_internal_errors(TRUE);
$rc = parent::__construct($version, $encoding);
$this->registerNodeClasses();
return $rc;
}
/**
* Reset XPath object so the clone gets a new instance when needed
*/
public function __clone() {
$this->registerNodeClasses();
$this->xp = new fDOMXPath($this);
foreach($this->prefixes as $prefix => $uri) {
$this->xp->registerNamespace($prefix, $uri);
}
}
/**
* @return string
*/
public function __toString() {
return $this->C14N();
}
/**
* Set Stream context options
*
* @param array $options Stream context options
*
* @return boolean true on success, false on failure
*/
public function setStreamContext(array $options) {
if (!count($options)) {
return FALSE;
}
$context = stream_context_create($options);
libxml_set_streams_context($context);
return TRUE;
}
/**
* Wrapper to DOMDocument load with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $fname File to load
* @param int|null $options LibXML Flags to pass
*
* @throws fDOMException
*
* @return bool|mixed
*/
public function load($fname, $options = LIBXML_NONET) {
if ($fname === '') {
throw new fDOMException('empty filename is not allowed', fDOMException::ParseError);
}
$this->xp = NULL;
$tmp = parent :: load($fname, $options);
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException("loading file '$fname' failed.", fDOMException::LoadError);
}
$this->registerNodeClasses();
return TRUE;
}
/**
* Wrapper to DOMDocument loadXML with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $source XML source code
* @param integer $options LibXML option flags
*
* @throws fDOMException
*
* @return boolean
*/
public function loadXML($source, $options = LIBXML_NONET) {
if ($source === '') {
throw new fDOMException('empty string not allowed', fDOMException::ParseError);
}
$this->xp = NULL;
$tmp = parent :: loadXML($source, $options);
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException('parsing string failed', fDOMException::ParseError);
}
$this->registerNodeClasses();
return TRUE;
}
/**
* Wrapper to DOMDocument loadHTMLFile with exception handling.
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $fname html file to load
* @param integer $options Options bitmask (@see DOMDocument::loadHTMLFile)
*
* @throws fDOMException
*
* @return boolean
*/
public function loadHTMLFile($fname, $options = NULL) {
if ($fname === '') {
throw new fDOMException('empty filename is not allowed', fDOMException::ParseError);
}
$this->xp = NULL;
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
if ($options !== NULL) {
throw new fDOMException('Passing options requires PHP 5.4.0+', fDOMException::LoadError);
}
$tmp = parent :: loadHTMLFile($fname);
} else {
$tmp = parent :: loadHTMLFile($fname, $options);
}
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException("loading html file '$fname' failed", fDOMException::LoadError);
}
$this->registerNodeClasses();
return TRUE;
}
/**
* Wrapper to DOMDocument loadHTML with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $source html source code
* @param integer $options Options bitmask (@see DOMDocument::loadHTML)
*
* @throws fDOMException
*
* @return boolean
*/
public function loadHTML($source, $options = NULL) {
if ($source === '') {
throw new fDOMException('empty string not allowed', fDOMException::ParseError);
}
$this->xp = NULL;
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
if ($options !== NULL) {
throw new fDOMException('Passing options requires PHP 5.4.0+', fDOMException::LoadError);
}
$tmp = parent :: loadHTML($source);
} else {
$tmp = parent :: loadHTML($source, $options);
}
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException('parsing html string failed', fDOMException::ParseError);
}
$this->registerNodeClasses();
return TRUE;
}
/**
* Wrapper to DOMDocument::save with exception handling
*
* @param string $filename filename to save to
* @param integer $options Options bitmask (@see DOMDocument::save)
*
* @throws fDOMException
*
* @return integer bytes saved
*/
public function save($filename, $options = NULL) {
$tmp = parent::save($filename, $options);
if (!$tmp) {
throw new fDOMException("Saving XML to file '$filename' failed", fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveHTML with exception handling
*
* @param \DOMNode|null $node Context DOMNode (optional)
*
* @throws fDOMException
*
* @return string html content
*/
public function saveHTML(\DOMNode $node = NULL) {
if (version_compare(PHP_VERSION, '5.3.6', '<') && $node !== NULL) {
throw new fDOMException('Passing a context node requires PHP 5.3.6+', fDOMException::SaveError);
}
$tmp = parent::saveHTML($node);
if (!$tmp) {
throw new fDOMException('Serializing to HTML failed', fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveHTMLfile with exception handling
*
* @param string $filename filename to save to
* @param integer $options Options bitmask (@see DOMDocument::saveHTMLFile)
*
* @throws fDOMException
*
* @return integer bytes saved
*/
public function saveHTMLFile($filename, $options = NULL) {
$tmp = parent::saveHTMLFile($filename, $options);
if (!$tmp) {
throw new fDOMException("Saving HTML to file '$filename' failed", fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveXML with exception handling
*
* @param \DOMNode $node node to start serializing at
* @param integer $options options flags as bitmask
*
* @throws fDOMException
*
* @return string serialized XML
*/
public function saveXML(\DOMNode $node = NULL, $options = NULL) {
try {
$tmp = parent::saveXML($node, $options);
if (!$tmp) {
throw new fDOMException('Serializing to XML failed', fDOMException::SaveError);
}
return $tmp;
} catch (\Exception $e) {
if (!$e instanceof fDOMException) {
throw new fDOMException($e->getMessage(), fDOMException::SaveError, $e);
}
throw $e;
}
}
/**
* get Instance of DOMXPath Object for current DOM
*
* @throws fDOMException
*
* @return fDOMXPath
*/
public function getDOMXPath() {
if (is_null($this->xp)) {
$this->xp = new fDOMXPath($this);
}
if (!$this->xp) {
throw new fDOMException('creating DOMXPath object failed.', fDOMException::NoDOMXPath);
}
return $this->xp;
}
/**
* Convert a given DOMNodeList into a DOMFragment
*
* @param \DOMNodeList $list The Nodelist to process
* @param boolean $move Signale if nodes are to be moved into fragment or not
*
* @return fDOMDocumentFragment
*/
public function nodeList2Fragment(\DOMNodeList $list, $move=FALSE) {
$frag = $this->createDocumentFragment();
/** @var fDOMNode $node */
foreach($list as $node) {
$frag->appendChild($move ? $node : $node->cloneNode(TRUE));
}
return $this->ensureIntance($frag);
}
/**
* Perform an xpath query
*
* @param String $q query string containing xpath
* @param \DOMNode|null $ctx (optional) Context DOMNode
* @param boolean $registerNodeNS Register flag pass through
*
* @return \DOMNodeList
*/
public function query($q, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->evaluate($q, $ctx, $registerNodeNS);
}
/**
* Perform an xpath query and return only the 1st match
*
* @param String $q query string containing xpath
* @param \DOMNode $ctx (optional) Context DOMNode
* @param boolean $registerNodeNS Register flag pass thru
*
* @return fDOMNode
*/
public function queryOne($q, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->queryOne($q, $ctx, $registerNodeNS);
}
/**
* Forwarder to fDOMXPath's prepare method allowing for easy and secure
* placeholder replacement comparable to sql's prepared statements
* .
* @param string $xpath String containing xpath with :placeholder markup
* @param array $valueMap array containing keys (:placeholder) and value pairs to be quoted
*
* @return string
*/
public function prepareQuery($xpath, array $valueMap) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->prepare($xpath, $valueMap);
}
/**
* Use a CSS Level 3 Selector string to query select nodes
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
$translator = new Translator();
$xpath = $translator->translate($selector);
if ($ctx !== NULL) {
$xpath = '.' . $xpath;
}
return $this->query($xpath, $ctx, $registerNodeNS);
}
/**
* Forward to DOMXPath->registerNamespace()
*
* @param string $prefix The prefix to use
* @param string $uri The uri to assign to this prefix
*
* @throws fDOMException
*
* @return void
*/
public function registerNamespace($prefix, $uri) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
if (!$this->xp->registerNamespace($prefix, $uri)) {
throw new fDOMException("Registering namespace '$uri' with prefix '$prefix' failed.", fDOMException::RegistrationFailed);
}
$this->prefixes[$prefix] = $uri;
}
/**
* Forward to DOMXPath->registerPHPFunctions()
*
* @param mixed $restrict array of function names or string with functionname to restrict callabilty to
*
* @throws fDOMException
*
* @return void
*/
public function registerPHPFunctions($restrict = NULL) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
$this->xp->registerPHPFunctions($restrict);
if (libxml_get_last_error()) {
throw new fDOMException("Registering php functions failed.", fDOMException::RegistrationFailed);
}
}
/**
* Create a new element in namespace defined by given prefix
*
* @param string $prefix Namespace prefix for node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement Reference to created fDOMElement
*/
public function createElementPrefix($prefix, $name, $content = NULL, $asTextNode = FALSE) {
if (!isset($this->prefixes[$prefix])) {
throw new fDOMException("'$prefix' not bound", fDOMException::UnboundPrefix);
}
return $this->createElementNS($this->prefixes[$prefix], $prefix.':'.$name, $content, $asTextNode);
}
/**
* Create a new fDOMElement and return it, optionally set content
*
* @param string $name Name of node to create
* @param null $content Content to set (optional)
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement Reference to created fDOMElement
*/
public function createElement($name, $content = NULL, $asTextnode = FALSE) {
try {
$node = parent::createElement($name);
if (!$node) {
throw new fDOMException("Creating element with name '$name' failed", fDOMException::NameInvalid);
}
if ($content !== NULL) {
if ($asTextnode) {
$node->appendChild($this->createTextnode($content));
} else {
$node->nodeValue = $content;
}
if (libxml_get_errors()) {
throw new fDOMException("Setting content value failed", fDOMException::SetFailedError);
}
}
return $this->ensureIntance($node);
} catch (\DOMException $e) {
throw new fDOMException("Creating elemnt with name '$name' failed", 0, $e);
}
}
/**
* Create a new fDOMElement within given namespace and return it
*
* @param string $namespace Namespace URI for node to create
* @param string $name Name of node to create
* @param string $content Content to set (optional)
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement
*/
public function createElementNS($namespace, $name, $content = NULL, $asTextNode = FALSE) {
$node = parent::createElementNS($namespace, $name);
if (!$node) {
throw new fDOMException("Creating element with name '$name' failed", fDOMException::NameInvalid);
}
if ($content !== NULL) {
if ($asTextNode) {
$node->appendChild($this->createTextnode($content));
} else {
$node->nodeValue = $content;
}
if (libxml_get_errors()) {
throw new fDOMException("Setting content value failed", fDOMException::SetFailedError);
}
}
return $this->ensureIntance($node);
}
/**
* @return fDOMDocumentFragment
*/
public function createDocumentFragment() {
return $this->ensureIntance(parent::createDocumentFragment());
}
/**
* Check if the given node is in the same document
*
* @param \DOMNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
if ($node instanceof \DOMDocument) {
return $this->isSameNode($node);
}
return $this->isSameNode($node->ownerDocument);
}
/**
* Create a new element and append it as documentElement
*
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextNode
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElement($name, $content = NULL, $asTextNode = FALSE) {
return $this->appendChild(
$this->createElement($name, $content, $asTextNode)
);
}
/**
* Create a new element in given namespace and append it as documentElement
*
* @param string $ns Namespace of node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextNode
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementNS($ns, $name, $content = NULL, $asTextNode = FALSE) {
return $this->appendChild(
$this->createElementNS($ns, $name, $content, $asTextNode)
);
}
/**
* This is a workaround for hhvm's broken registerNodeClass handling
* (https://github.com/facebook/hhvm/issues/1848)
*
* @param \DOMNode $node
*
* @return \DOMNode
*/
private function ensureIntance(\DOMNode $node) {
if ($node instanceof fDOMNode || $node instanceof fDOMElement || $node instanceof fDOMDocumentFragment) {
return $node;
}
return $this->importNode($node, TRUE);
}
/**
* Register replacements
*
* Called from constructor and, as a workaround for (https://github.com/facebook/hhvm/issues/5412),
* after load(), loadXML(), loadHTML() and loadHTMLFile()
*/
private function registerNodeClasses() {
$this->registerNodeClass('DOMDocument', get_called_class());
$this->registerNodeClass('DOMNode', 'TheSeer\fDOM\fDOMNode');
$this->registerNodeClass('DOMElement', 'TheSeer\fDOM\fDOMElement');
$this->registerNodeClass('DOMDocumentFragment', 'TheSeer\fDOM\fDOMDocumentFragment');
}
} // fDOMDocument
}
<?php
namespace TheSeer\fDOM\CSS {
class NotRule implements RuleInterface {
/**
* @var Translator
*/
private $translator;
/**
* @param Translator $translator
*/
public function __construct(Translator $translator) {
$this->translator = $translator;
}
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/([a-zA-Z0-9\_\-\*]+):not\(([^\)]*)\)/',
array($this, 'callback'),
$selector
);
}
/**
* @param array $matches
*
* @return string
*/
private function callback(array $matches) {
$subresult = preg_replace(
'/^[^\[]+\[([^\]]*)\].*$/',
'$1',
$this->translator->translate($matches[2])
);
return $matches[1] . '[not(' . $subresult . ')]';
}
}
}
<?php
namespace TheSeer\fDOM\CSS {
class DollarEqualRule implements RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/\[([a-zA-Z0-9\_\-]+)\$=([^\]]+)\]/',
array($this, 'callback'),
$selector
);
}
/**
* Build query from matches.
*
* @param array $matches
*
* @return string
*/
private function callback(array $matches) {
return '[substring(@' . $matches[1] . ',string-length(@' . $matches[1] . ')-' . (strlen($matches[2]) - 3) . ')=' . $matches[1] . ']';
}
}
}
<?php
namespace TheSeer\fDOM\CSS {
class RegexRule implements RuleInterface {
/**
* @var string
*/
private $regex;
/**
* @var string
*/
private $replacement;
/**
* @param string $regex
* @param string $replacement
*/
public function __construct($regex, $replacement) {
$this->regex = $regex;
$this->replacement = $replacement;
}
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace($this->regex, $this->replacement, $selector);
}
}
}
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright 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 ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM\CSS {
/**
* Class Translator
*
* The regular expressions used in this class are heavily inspired by and mostly adopted from
* the css2xpath.js code by Andrea Giammarchi (http://code.google.com/p/css2xpath/).
* The JavaScript version (css2xpath.js) is licensed under the MIT License
*
*/
class Translator {
/**
* @var array
*/
private $rules;
/**
* @param string $selector A CSS Selector string
*
* @return string
*/
public function translate($selector) {
foreach($this->getRules() as $rule) {
/** @var RuleInterface $rule */
$selector = $rule->apply($selector);
}
return '//' . $selector;
}
/**
* @return array
*/
private function getRules() {
if ($this->rules != NULL) {
return $this->rules;
}
$this->rules = array(
// prefix|name
new RegexRule('/([a-zA-Z0-9\_\-\*]+)\|([a-zA-Z0-9\_\-\*]+)/', '$1:$2'),
// add @ for attribs
new RegexRule("/\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]/", '[@$1$2]'),
// multiple queries
new RegexRule("/\s*,\s*/", '|'),
// , + ~ >
new RegexRule("/\s*(\+|~|>)\s*/", '$1'),
//* ~ + >
new RegexRule("/([a-zA-Z0-9\_\-\*])~([a-zA-Z0-9\_\-\*])/", '$1/following-sibling::$2'),
new RegexRule("/([a-zA-Z0-9\_\-\*])\+([a-zA-Z0-9\_\-\*])/", '$1/following-sibling::*[1]/self::$2'),
new RegexRule("/([a-zA-Z0-9\_\-\*])>([a-zA-Z0-9\_\-\*])/", '$1/$2'),
// all unescaped stuff escaped
new RegexRule("/\[([^=]+)=([^'|'][^\]]*)\]/", '[$1="$2"]'),
// all descendant or self to //
new RegexRule("/(^|[^a-zA-Z0-9\_\-\*])(#|\.)([a-zA-Z0-9\_\-]+)/", '$1*$2$3'),
new RegexRule("/([\>\+\|\~\,\s])([a-zA-Z\*]+)/", '$1//$2'),
new RegexRule("/\s+\/\//", '//'),
// :first-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):first-child/", '*[1]/self::$1'),
// :last-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):last-child/", '$1[not(following-sibling::*)]'),
// :only-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):only-child/", '*[last()=1]/self::$1'),
// :empty
new RegexRule("/([a-zA-Z0-9\_\-\*]+):empty/", '$1[not(*) and not(normalize-space())]'),
// :not
new NotRule($this),
// :nth-child
new NthChildRule(),
// :contains(selectors)
new RegexRule('/:contains\(([^\)]*)\)/', '[contains(string(.),"$1")]'),
// |= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\|=([^\]]+)\]/", '[@$1=$2 or starts-with(@$1,concat($2,"-"))]'),
// *= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\*=([^\]]+)\]/", '[contains(@$1,$2)]'),
// ~= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)~=([^\]]+)\]/", '[contains(concat(" ",normalize-space(@$1)," "),concat(" ",$2," "))]'),
// ^= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\^=([^\]]+)\]/", '[starts-with(@$1,$2)]'),
// $= attrib
new DollarEqualRule(),
// != attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\!=([^\]]+)\]/", '[not(@$1) or @$1!=$2]'),
// ids and classes
new RegexRule("/#([a-zA-Z0-9\_\-]+)/", '[@id="$1"]'),
new RegexRule("/\.([a-zA-Z0-9\_\-]+)/", '[contains(concat(" ",normalize-space(@class)," ")," $1 ")]'),
// normalize multiple filters
new RegexRule("/\]\[([^\]]+)/", ' and ($1)')
);
return $this->rules;
}
}
}
<?php
namespace TheSeer\fDOM\CSS {
interface RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector);
}
}
<?php
namespace TheSeer\fDOM\CSS {
class NthChildRule implements RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/([a-zA-Z0-9\_\-\*]+):nth-child\(([^\)]*)\)/',
array($this, 'callback'),
$selector
);
}
/**
* @param array $matches
*
* @return string
*/
private function callback(array $matches) {
switch($matches[2]){
case 'n': {
return $matches[1];
}
case 'even': {
return '*[position() mod 2=0 and position()>=0]/self::' . $matches[1];
}
case 'odd': {
return $matches[1] . '[(count(preceding-sibling::*) + 1) mod 2=1]';
}
default: {
$b = !isset($matches[2]) || empty($matches[2]) ? '0' : $matches[2];
$b = preg_replace('/^([0-9]*)n.*?([0-9]*)$/', '$1+$2', $b);
$b = explode('+', $b);
if (!isset($b[1])) {
$b[1] = '0';
}
return '*[(position()-' . $b[1] . ') mod ' . $b[0] . '=0 and position()>=' . $b[1] . ']/self::' . $matches[1];
}
}
}
}
}
<?php
return array(
'A' => 'a',
'B' => 'b',
'C' => 'c',
'D' => 'd',
'E' => 'e',
'F' => 'f',
'G' => 'g',
'H' => 'h',
'I' => 'i',
'J' => 'j',
'K' => 'k',
'L' => 'l',
'M' => 'm',
'N' => 'n',
'O' => 'o',
'P' => 'p',
'Q' => 'q',
'R' => 'r',
'S' => 's',
'T' => 't',
'U' => 'u',
'V' => 'v',
'W' => 'w',
'X' => 'x',
'Y' => 'y',
'Z' => 'z',
'À' => 'à',
'Á' => 'á',
'Â' => 'â',
'Ã' => 'ã',
'Ä' => 'ä',
'Å' => 'å',
'Æ' => 'æ',
'Ç' => 'ç',
'È' => 'è',
'É' => 'é',
'Ê' => 'ê',
'Ë' => 'ë',
'Ì' => 'ì',
'Í' => 'í',
'Î' => 'î',
'Ï' => 'ï',
'Ð' => 'ð',
'Ñ' => 'ñ',
'Ò' => 'ò',
'Ó' => 'ó',
'Ô' => 'ô',
'Õ' => 'õ',
'Ö' => 'ö',
'Ø' => 'ø',
'Ù' => 'ù',
'Ú' => 'ú',
'Û' => 'û',
'Ü' => 'ü',
'Ý' => 'ý',
'Þ' => 'þ',
'Ā' => 'ā',
'Ă' => 'ă',
'Ą' => 'ą',
'Ć' => 'ć',
'Ĉ' => 'ĉ',
'Ċ' => 'ċ',
'Č' => 'č',
'Ď' => 'ď',
'Đ' => 'đ',
'Ē' => 'ē',
'Ĕ' => 'ĕ',
'Ė' => 'ė',
'Ę' => 'ę',
'Ě' => 'ě',
'Ĝ' => 'ĝ',
'Ğ' => 'ğ',
'Ġ' => 'ġ',
'Ģ' => 'ģ',
'Ĥ' => 'ĥ',
'Ħ' => 'ħ',
'Ĩ' => 'ĩ',
'Ī' => 'ī',
'Ĭ' => 'ĭ',
'Į' => 'į',
'İ' => 'i',
'IJ' => 'ij',
'Ĵ' => 'ĵ',
'Ķ' => 'ķ',
'Ĺ' => 'ĺ',
'Ļ' => 'ļ',
'Ľ' => 'ľ',
'Ŀ' => 'ŀ',
'Ł' => 'ł',
'Ń' => 'ń',
'Ņ' => 'ņ',
'Ň' => 'ň',
'Ŋ' => 'ŋ',
'Ō' => 'ō',
'Ŏ' => 'ŏ',
'Ő' => 'ő',
'Œ' => 'œ',
'Ŕ' => 'ŕ',
'Ŗ' => 'ŗ',
'Ř' => 'ř',
'Ś' => 'ś',
'Ŝ' => 'ŝ',
'Ş' => 'ş',
'Š' => 'š',
'Ţ' => 'ţ',
'Ť' => 'ť',
'Ŧ' => 'ŧ',
'Ũ' => 'ũ',
'Ū' => 'ū',
'Ŭ' => 'ŭ',
'Ů' => 'ů',
'Ű' => 'ű',
'Ų' => 'ų',
'Ŵ' => 'ŵ',
'Ŷ' => 'ŷ',
'Ÿ' => 'ÿ',
'Ź' => 'ź',
'Ż' => 'ż',
'Ž' => 'ž',
'Ɓ' => 'ɓ',
'Ƃ' => 'ƃ',
'Ƅ' => 'ƅ',
'Ɔ' => 'ɔ',
'Ƈ' => 'ƈ',
'Ɖ' => 'ɖ',
'Ɗ' => 'ɗ',
'Ƌ' => 'ƌ',
'Ǝ' => 'ǝ',
'Ə' => 'ə',
'Ɛ' => 'ɛ',
'Ƒ' => 'ƒ',
'Ɠ' => 'ɠ',
'Ɣ' => 'ɣ',
'Ɩ' => 'ɩ',
'Ɨ' => 'ɨ',
'Ƙ' => 'ƙ',
'Ɯ' => 'ɯ',
'Ɲ' => 'ɲ',
'Ɵ' => 'ɵ',
'Ơ' => 'ơ',
'Ƣ' => 'ƣ',
'Ƥ' => 'ƥ',
'Ʀ' => 'ʀ',
'Ƨ' => 'ƨ',
'Ʃ' => 'ʃ',
'Ƭ' => 'ƭ',
'Ʈ' => 'ʈ',
'Ư' => 'ư',
'Ʊ' => 'ʊ',
'Ʋ' => 'ʋ',
'Ƴ' => 'ƴ',
'Ƶ' => 'ƶ',
'Ʒ' => 'ʒ',
'Ƹ' => 'ƹ',
'Ƽ' => 'ƽ',
'DŽ' => 'dž',
'Dž' => 'dž',
'LJ' => 'lj',
'Lj' => 'lj',
'NJ' => 'nj',
'Nj' => 'nj',
'Ǎ' => 'ǎ',
'Ǐ' => 'ǐ',
'Ǒ' => 'ǒ',
'Ǔ' => 'ǔ',
'Ǖ' => 'ǖ',
'Ǘ' => 'ǘ',
'Ǚ' => 'ǚ',
'Ǜ' => 'ǜ',
'Ǟ' => 'ǟ',
'Ǡ' => 'ǡ',
'Ǣ' => 'ǣ',
'Ǥ' => 'ǥ',
'Ǧ' => 'ǧ',
'Ǩ' => 'ǩ',
'Ǫ' => 'ǫ',
'Ǭ' => 'ǭ',
'Ǯ' => 'ǯ',
'DZ' => 'dz',
'Dz' => 'dz',
'Ǵ' => 'ǵ',
'Ƕ' => 'ƕ',
'Ƿ' => 'ƿ',
'Ǹ' => 'ǹ',
'Ǻ' => 'ǻ',
'Ǽ' => 'ǽ',
'Ǿ' => 'ǿ',
'Ȁ' => 'ȁ',
'Ȃ' => 'ȃ',
'Ȅ' => 'ȅ',
'Ȇ' => 'ȇ',
'Ȉ' => 'ȉ',
'Ȋ' => 'ȋ',
'Ȍ' => 'ȍ',
'Ȏ' => 'ȏ',
'Ȑ' => 'ȑ',
'Ȓ' => 'ȓ',
'Ȕ' => 'ȕ',
'Ȗ' => 'ȗ',
'Ș' => 'ș',
'Ț' => 'ț',
'Ȝ' => 'ȝ',
'Ȟ' => 'ȟ',
'Ƞ' => 'ƞ',
'Ȣ' => 'ȣ',
'Ȥ' => 'ȥ',
'Ȧ' => 'ȧ',
'Ȩ' => 'ȩ',
'Ȫ' => 'ȫ',
'Ȭ' => 'ȭ',
'Ȯ' => 'ȯ',
'Ȱ' => 'ȱ',
'Ȳ' => 'ȳ',
'Ⱥ' => 'ⱥ',
'Ȼ' => 'ȼ',
'Ƚ' => 'ƚ',
'Ⱦ' => 'ⱦ',
'Ɂ' => 'ɂ',
'Ƀ' => 'ƀ',
'Ʉ' => 'ʉ',
'Ʌ' => 'ʌ',
'Ɇ' => 'ɇ',
'Ɉ' => 'ɉ',
'Ɋ' => 'ɋ',
'Ɍ' => 'ɍ',
'Ɏ' => 'ɏ',
'Ͱ' => 'ͱ',
'Ͳ' => 'ͳ',
'Ͷ' => 'ͷ',
'Ϳ' => 'ϳ',
'Ά' => 'ά',
'Έ' => 'έ',
'Ή' => 'ή',
'Ί' => 'ί',
'Ό' => 'ό',
'Ύ' => 'ύ',
'Ώ' => 'ώ',
'Α' => 'α',
'Β' => 'β',
'Γ' => 'γ',
'Δ' => 'δ',
'Ε' => 'ε',
'Ζ' => 'ζ',
'Η' => 'η',
'Θ' => 'θ',
'Ι' => 'ι',
'Κ' => 'κ',
'Λ' => 'λ',
'Μ' => 'μ',
'Ν' => 'ν',
'Ξ' => 'ξ',
'Ο' => 'ο',
'Π' => 'π',
'Ρ' => 'ρ',
'Σ' => 'σ',
'Τ' => 'τ',
'Υ' => 'υ',
'Φ' => 'φ',
'Χ' => 'χ',
'Ψ' => 'ψ',
'Ω' => 'ω',
'Ϊ' => 'ϊ',
'Ϋ' => 'ϋ',
'Ϗ' => 'ϗ',
'Ϙ' => 'ϙ',
'Ϛ' => 'ϛ',
'Ϝ' => 'ϝ',
'Ϟ' => 'ϟ',
'Ϡ' => 'ϡ',
'Ϣ' => 'ϣ',
'Ϥ' => 'ϥ',
'Ϧ' => 'ϧ',
'Ϩ' => 'ϩ',
'Ϫ' => 'ϫ',
'Ϭ' => 'ϭ',
'Ϯ' => 'ϯ',
'ϴ' => 'θ',
'Ϸ' => 'ϸ',
'Ϲ' => 'ϲ',
'Ϻ' => 'ϻ',
'Ͻ' => 'ͻ',
'Ͼ' => 'ͼ',
'Ͽ' => 'ͽ',
'Ѐ' => 'ѐ',
'Ё' => 'ё',
'Ђ' => 'ђ',
'Ѓ' => 'ѓ',
'Є' => 'є',
'Ѕ' => 'ѕ',
'І' => 'і',
'Ї' => 'ї',
'Ј' => 'ј',
'Љ' => 'љ',
'Њ' => 'њ',
'Ћ' => 'ћ',
'Ќ' => 'ќ',
'Ѝ' => 'ѝ',
'Ў' => 'ў',
'Џ' => 'џ',
'А' => 'а',
'Б' => 'б',
'В' => 'в',
'Г' => 'г',
'Д' => 'д',
'Е' => 'е',
'Ж' => 'ж',
'З' => 'з',
'И' => 'и',
'Й' => 'й',
'К' => 'к',
'Л' => 'л',
'М' => 'м',
'Н' => 'н',
'О' => 'о',
'П' => 'п',
'Р' => 'р',
'С' => 'с',
'Т' => 'т',
'У' => 'у',
'Ф' => 'ф',
'Х' => 'х',
'Ц' => 'ц',
'Ч' => 'ч',
'Ш' => 'ш',
'Щ' => 'щ',
'Ъ' => 'ъ',
'Ы' => 'ы',
'Ь' => 'ь',
'Э' => 'э',
'Ю' => 'ю',
'Я' => 'я',
'Ѡ' => 'ѡ',
'Ѣ' => 'ѣ',
'Ѥ' => 'ѥ',
'Ѧ' => 'ѧ',
'Ѩ' => 'ѩ',
'Ѫ' => 'ѫ',
'Ѭ' => 'ѭ',
'Ѯ' => 'ѯ',
'Ѱ' => 'ѱ',
'Ѳ' => 'ѳ',
'Ѵ' => 'ѵ',
'Ѷ' => 'ѷ',
'Ѹ' => 'ѹ',
'Ѻ' => 'ѻ',
'Ѽ' => 'ѽ',
'Ѿ' => 'ѿ',
'Ҁ' => 'ҁ',
'Ҋ' => 'ҋ',
'Ҍ' => 'ҍ',
'Ҏ' => 'ҏ',
'Ґ' => 'ґ',
'Ғ' => 'ғ',
'Ҕ' => 'ҕ',
'Җ' => 'җ',
'Ҙ' => 'ҙ',
'Қ' => 'қ',
'Ҝ' => 'ҝ',
'Ҟ' => 'ҟ',
'Ҡ' => 'ҡ',
'Ң' => 'ң',
'Ҥ' => 'ҥ',
'Ҧ' => 'ҧ',
'Ҩ' => 'ҩ',
'Ҫ' => 'ҫ',
'Ҭ' => 'ҭ',
'Ү' => 'ү',
'Ұ' => 'ұ',
'Ҳ' => 'ҳ',
'Ҵ' => 'ҵ',
'Ҷ' => 'ҷ',
'Ҹ' => 'ҹ',
'Һ' => 'һ',
'Ҽ' => 'ҽ',
'Ҿ' => 'ҿ',
'Ӏ' => 'ӏ',
'Ӂ' => 'ӂ',
'Ӄ' => 'ӄ',
'Ӆ' => 'ӆ',
'Ӈ' => 'ӈ',
'Ӊ' => 'ӊ',
'Ӌ' => 'ӌ',
'Ӎ' => 'ӎ',
'Ӑ' => 'ӑ',
'Ӓ' => 'ӓ',
'Ӕ' => 'ӕ',
'Ӗ' => 'ӗ',
'Ә' => 'ә',
'Ӛ' => 'ӛ',
'Ӝ' => 'ӝ',
'Ӟ' => 'ӟ',
'Ӡ' => 'ӡ',
'Ӣ' => 'ӣ',
'Ӥ' => 'ӥ',
'Ӧ' => 'ӧ',
'Ө' => 'ө',
'Ӫ' => 'ӫ',
'Ӭ' => 'ӭ',
'Ӯ' => 'ӯ',
'Ӱ' => 'ӱ',
'Ӳ' => 'ӳ',
'Ӵ' => 'ӵ',
'Ӷ' => 'ӷ',
'Ӹ' => 'ӹ',
'Ӻ' => 'ӻ',
'Ӽ' => 'ӽ',
'Ӿ' => 'ӿ',
'Ԁ' => 'ԁ',
'Ԃ' => 'ԃ',
'Ԅ' => 'ԅ',
'Ԇ' => 'ԇ',
'Ԉ' => 'ԉ',
'Ԋ' => 'ԋ',
'Ԍ' => 'ԍ',
'Ԏ' => 'ԏ',
'Ԑ' => 'ԑ',
'Ԓ' => 'ԓ',
'Ԕ' => 'ԕ',
'Ԗ' => 'ԗ',
'Ԙ' => 'ԙ',
'Ԛ' => 'ԛ',
'Ԝ' => 'ԝ',
'Ԟ' => 'ԟ',
'Ԡ' => 'ԡ',
'Ԣ' => 'ԣ',
'Ԥ' => 'ԥ',
'Ԧ' => 'ԧ',
'Ԩ' => 'ԩ',
'Ԫ' => 'ԫ',
'Ԭ' => 'ԭ',
'Ԯ' => 'ԯ',
'Ա' => 'ա',
'Բ' => 'բ',
'Գ' => 'գ',
'Դ' => 'դ',
'Ե' => 'ե',
'Զ' => 'զ',
'Է' => 'է',
'Ը' => 'ը',
'Թ' => 'թ',
'Ժ' => 'ժ',
'Ի' => 'ի',
'Լ' => 'լ',
'Խ' => 'խ',
'Ծ' => 'ծ',
'Կ' => 'կ',
'Հ' => 'հ',
'Ձ' => 'ձ',
'Ղ' => 'ղ',
'Ճ' => 'ճ',
'Մ' => 'մ',
'Յ' => 'յ',
'Ն' => 'ն',
'Շ' => 'շ',
'Ո' => 'ո',
'Չ' => 'չ',
'Պ' => 'պ',
'Ջ' => 'ջ',
'Ռ' => 'ռ',
'Ս' => 'ս',
'Վ' => 'վ',
'Տ' => 'տ',
'Ր' => 'ր',
'Ց' => 'ց',
'Ւ' => 'ւ',
'Փ' => 'փ',
'Ք' => 'ք',
'Օ' => 'օ',
'Ֆ' => 'ֆ',
'Ⴀ' => 'ⴀ',
'Ⴁ' => 'ⴁ',
'Ⴂ' => 'ⴂ',
'Ⴃ' => 'ⴃ',
'Ⴄ' => 'ⴄ',
'Ⴅ' => 'ⴅ',
'Ⴆ' => 'ⴆ',
'Ⴇ' => 'ⴇ',
'Ⴈ' => 'ⴈ',
'Ⴉ' => 'ⴉ',
'Ⴊ' => 'ⴊ',
'Ⴋ' => 'ⴋ',
'Ⴌ' => 'ⴌ',
'Ⴍ' => 'ⴍ',
'Ⴎ' => 'ⴎ',
'Ⴏ' => 'ⴏ',
'Ⴐ' => 'ⴐ',
'Ⴑ' => 'ⴑ',
'Ⴒ' => 'ⴒ',
'Ⴓ' => 'ⴓ',
'Ⴔ' => 'ⴔ',
'Ⴕ' => 'ⴕ',
'Ⴖ' => 'ⴖ',
'Ⴗ' => 'ⴗ',
'Ⴘ' => 'ⴘ',
'Ⴙ' => 'ⴙ',
'Ⴚ' => 'ⴚ',
'Ⴛ' => 'ⴛ',
'Ⴜ' => 'ⴜ',
'Ⴝ' => 'ⴝ',
'Ⴞ' => 'ⴞ',
'Ⴟ' => 'ⴟ',
'Ⴠ' => 'ⴠ',
'Ⴡ' => 'ⴡ',
'Ⴢ' => 'ⴢ',
'Ⴣ' => 'ⴣ',
'Ⴤ' => 'ⴤ',
'Ⴥ' => 'ⴥ',
'Ⴧ' => 'ⴧ',
'Ⴭ' => 'ⴭ',
'Ḁ' => 'ḁ',
'Ḃ' => 'ḃ',
'Ḅ' => 'ḅ',
'Ḇ' => 'ḇ',
'Ḉ' => 'ḉ',
'Ḋ' => 'ḋ',
'Ḍ' => 'ḍ',
'Ḏ' => 'ḏ',
'Ḑ' => 'ḑ',
'Ḓ' => 'ḓ',
'Ḕ' => 'ḕ',
'Ḗ' => 'ḗ',
'Ḙ' => 'ḙ',
'Ḛ' => 'ḛ',
'Ḝ' => 'ḝ',
'Ḟ' => 'ḟ',
'Ḡ' => 'ḡ',
'Ḣ' => 'ḣ',
'Ḥ' => 'ḥ',
'Ḧ' => 'ḧ',
'Ḩ' => 'ḩ',
'Ḫ' => 'ḫ',
'Ḭ' => 'ḭ',
'Ḯ' => 'ḯ',
'Ḱ' => 'ḱ',
'Ḳ' => 'ḳ',
'Ḵ' => 'ḵ',
'Ḷ' => 'ḷ',
'Ḹ' => 'ḹ',
'Ḻ' => 'ḻ',
'Ḽ' => 'ḽ',
'Ḿ' => 'ḿ',
'Ṁ' => 'ṁ',
'Ṃ' => 'ṃ',
'Ṅ' => 'ṅ',
'Ṇ' => 'ṇ',
'Ṉ' => 'ṉ',
'Ṋ' => 'ṋ',
'Ṍ' => 'ṍ',
'Ṏ' => 'ṏ',
'Ṑ' => 'ṑ',
'Ṓ' => 'ṓ',
'Ṕ' => 'ṕ',
'Ṗ' => 'ṗ',
'Ṙ' => 'ṙ',
'Ṛ' => 'ṛ',
'Ṝ' => 'ṝ',
'Ṟ' => 'ṟ',
'Ṡ' => 'ṡ',
'Ṣ' => 'ṣ',
'Ṥ' => 'ṥ',
'Ṧ' => 'ṧ',
'Ṩ' => 'ṩ',
'Ṫ' => 'ṫ',
'Ṭ' => 'ṭ',
'Ṯ' => 'ṯ',
'Ṱ' => 'ṱ',
'Ṳ' => 'ṳ',
'Ṵ' => 'ṵ',
'Ṷ' => 'ṷ',
'Ṹ' => 'ṹ',
'Ṻ' => 'ṻ',
'Ṽ' => 'ṽ',
'Ṿ' => 'ṿ',
'Ẁ' => 'ẁ',
'Ẃ' => 'ẃ',
'Ẅ' => 'ẅ',
'Ẇ' => 'ẇ',
'Ẉ' => 'ẉ',
'Ẋ' => 'ẋ',
'Ẍ' => 'ẍ',
'Ẏ' => 'ẏ',
'Ẑ' => 'ẑ',
'Ẓ' => 'ẓ',
'Ẕ' => 'ẕ',
'ẞ' => 'ß',
'Ạ' => 'ạ',
'Ả' => 'ả',
'Ấ' => 'ấ',
'Ầ' => 'ầ',
'Ẩ' => 'ẩ',
'Ẫ' => 'ẫ',
'Ậ' => 'ậ',
'Ắ' => 'ắ',
'Ằ' => 'ằ',
'Ẳ' => 'ẳ',
'Ẵ' => 'ẵ',
'Ặ' => 'ặ',
'Ẹ' => 'ẹ',
'Ẻ' => 'ẻ',
'Ẽ' => 'ẽ',
'Ế' => 'ế',
'Ề' => 'ề',
'Ể' => 'ể',
'Ễ' => 'ễ',
'Ệ' => 'ệ',
'Ỉ' => 'ỉ',
'Ị' => 'ị',
'Ọ' => 'ọ',
'Ỏ' => 'ỏ',
'Ố' => 'ố',
'Ồ' => 'ồ',
'Ổ' => 'ổ',
'Ỗ' => 'ỗ',
'Ộ' => 'ộ',
'Ớ' => 'ớ',
'Ờ' => 'ờ',
'Ở' => 'ở',
'Ỡ' => 'ỡ',
'Ợ' => 'ợ',
'Ụ' => 'ụ',
'Ủ' => 'ủ',
'Ứ' => 'ứ',
'Ừ' => 'ừ',
'Ử' => 'ử',
'Ữ' => 'ữ',
'Ự' => 'ự',
'Ỳ' => 'ỳ',
'Ỵ' => 'ỵ',
'Ỷ' => 'ỷ',
'Ỹ' => 'ỹ',
'Ỻ' => 'ỻ',
'Ỽ' => 'ỽ',
'Ỿ' => 'ỿ',
'Ἀ' => 'ἀ',
'Ἁ' => 'ἁ',
'Ἂ' => 'ἂ',
'Ἃ' => 'ἃ',
'Ἄ' => 'ἄ',
'Ἅ' => 'ἅ',
'Ἆ' => 'ἆ',
'Ἇ' => 'ἇ',
'Ἐ' => 'ἐ',
'Ἑ' => 'ἑ',
'Ἒ' => 'ἒ',
'Ἓ' => 'ἓ',
'Ἔ' => 'ἔ',
'Ἕ' => 'ἕ',
'Ἠ' => 'ἠ',
'Ἡ' => 'ἡ',
'Ἢ' => 'ἢ',
'Ἣ' => 'ἣ',
'Ἤ' => 'ἤ',
'Ἥ' => 'ἥ',
'Ἦ' => 'ἦ',
'Ἧ' => 'ἧ',
'Ἰ' => 'ἰ',
'Ἱ' => 'ἱ',
'Ἲ' => 'ἲ',
'Ἳ' => 'ἳ',
'Ἴ' => 'ἴ',
'Ἵ' => 'ἵ',
'Ἶ' => 'ἶ',
'Ἷ' => 'ἷ',
'Ὀ' => 'ὀ',
'Ὁ' => 'ὁ',
'Ὂ' => 'ὂ',
'Ὃ' => 'ὃ',
'Ὄ' => 'ὄ',
'Ὅ' => 'ὅ',
'Ὑ' => 'ὑ',
'Ὓ' => 'ὓ',
'Ὕ' => 'ὕ',
'Ὗ' => 'ὗ',
'Ὠ' => 'ὠ',
'Ὡ' => 'ὡ',
'Ὢ' => 'ὢ',
'Ὣ' => 'ὣ',
'Ὤ' => 'ὤ',
'Ὥ' => 'ὥ',
'Ὦ' => 'ὦ',
'Ὧ' => 'ὧ',
'ᾈ' => 'ᾀ',
'ᾉ' => 'ᾁ',
'ᾊ' => 'ᾂ',
'ᾋ' => 'ᾃ',
'ᾌ' => 'ᾄ',
'ᾍ' => 'ᾅ',
'ᾎ' => 'ᾆ',
'ᾏ' => 'ᾇ',
'ᾘ' => 'ᾐ',
'ᾙ' => 'ᾑ',
'ᾚ' => 'ᾒ',
'ᾛ' => 'ᾓ',
'ᾜ' => 'ᾔ',
'ᾝ' => 'ᾕ',
'ᾞ' => 'ᾖ',
'ᾟ' => 'ᾗ',
'ᾨ' => 'ᾠ',
'ᾩ' => 'ᾡ',
'ᾪ' => 'ᾢ',
'ᾫ' => 'ᾣ',
'ᾬ' => 'ᾤ',
'ᾭ' => 'ᾥ',
'ᾮ' => 'ᾦ',
'ᾯ' => 'ᾧ',
'Ᾰ' => 'ᾰ',
'Ᾱ' => 'ᾱ',
'Ὰ' => 'ὰ',
'Ά' => 'ά',
'ᾼ' => 'ᾳ',
'Ὲ' => 'ὲ',
'Έ' => 'έ',
'Ὴ' => 'ὴ',
'Ή' => 'ή',
'ῌ' => 'ῃ',
'Ῐ' => 'ῐ',
'Ῑ' => 'ῑ',
'Ὶ' => 'ὶ',
'Ί' => 'ί',
'Ῠ' => 'ῠ',
'Ῡ' => 'ῡ',
'Ὺ' => 'ὺ',
'Ύ' => 'ύ',
'Ῥ' => 'ῥ',
'Ὸ' => 'ὸ',
'Ό' => 'ό',
'Ὼ' => 'ὼ',
'Ώ' => 'ώ',
'ῼ' => 'ῳ',
'Ω' => 'ω',
'' => 'k',
'Å' => 'å',
'Ⅎ' => 'ⅎ',
'' => '',
'Ⅱ' => 'ⅱ',
'Ⅲ' => 'ⅲ',
'Ⅳ' => 'ⅳ',
'' => '',
'Ⅵ' => 'ⅵ',
'Ⅶ' => 'ⅶ',
'Ⅷ' => 'ⅷ',
'Ⅸ' => 'ⅸ',
'' => '',
'Ⅺ' => 'ⅺ',
'Ⅻ' => 'ⅻ',
'' => '',
'' => '',
'' => '',
'' => 'ⅿ',
'Ↄ' => 'ↄ',
'Ⓐ' => 'ⓐ',
'Ⓑ' => 'ⓑ',
'Ⓒ' => 'ⓒ',
'Ⓓ' => 'ⓓ',
'Ⓔ' => 'ⓔ',
'Ⓕ' => 'ⓕ',
'Ⓖ' => 'ⓖ',
'Ⓗ' => 'ⓗ',
'Ⓘ' => 'ⓘ',
'Ⓙ' => 'ⓙ',
'Ⓚ' => 'ⓚ',
'Ⓛ' => 'ⓛ',
'Ⓜ' => 'ⓜ',
'Ⓝ' => 'ⓝ',
'Ⓞ' => 'ⓞ',
'Ⓟ' => 'ⓟ',
'Ⓠ' => 'ⓠ',
'Ⓡ' => 'ⓡ',
'Ⓢ' => 'ⓢ',
'Ⓣ' => 'ⓣ',
'Ⓤ' => 'ⓤ',
'Ⓥ' => 'ⓥ',
'Ⓦ' => 'ⓦ',
'Ⓧ' => 'ⓧ',
'Ⓨ' => 'ⓨ',
'Ⓩ' => 'ⓩ',
'Ⰰ' => 'ⰰ',
'Ⰱ' => 'ⰱ',
'Ⰲ' => 'ⰲ',
'Ⰳ' => 'ⰳ',
'Ⰴ' => 'ⰴ',
'Ⰵ' => 'ⰵ',
'Ⰶ' => 'ⰶ',
'Ⰷ' => 'ⰷ',
'Ⰸ' => 'ⰸ',
'Ⰹ' => 'ⰹ',
'Ⰺ' => 'ⰺ',
'Ⰻ' => 'ⰻ',
'Ⰼ' => 'ⰼ',
'Ⰽ' => 'ⰽ',
'Ⰾ' => 'ⰾ',
'Ⰿ' => 'ⰿ',
'Ⱀ' => 'ⱀ',
'Ⱁ' => 'ⱁ',
'Ⱂ' => 'ⱂ',
'Ⱃ' => 'ⱃ',
'Ⱄ' => 'ⱄ',
'Ⱅ' => 'ⱅ',
'Ⱆ' => 'ⱆ',
'Ⱇ' => 'ⱇ',
'Ⱈ' => 'ⱈ',
'Ⱉ' => 'ⱉ',
'Ⱊ' => 'ⱊ',
'Ⱋ' => 'ⱋ',
'Ⱌ' => 'ⱌ',
'Ⱍ' => 'ⱍ',
'Ⱎ' => 'ⱎ',
'Ⱏ' => 'ⱏ',
'Ⱐ' => 'ⱐ',
'Ⱑ' => 'ⱑ',
'Ⱒ' => 'ⱒ',
'Ⱓ' => 'ⱓ',
'Ⱔ' => 'ⱔ',
'Ⱕ' => 'ⱕ',
'Ⱖ' => 'ⱖ',
'Ⱗ' => 'ⱗ',
'Ⱘ' => 'ⱘ',
'Ⱙ' => 'ⱙ',
'Ⱚ' => 'ⱚ',
'Ⱛ' => 'ⱛ',
'Ⱜ' => 'ⱜ',
'Ⱝ' => 'ⱝ',
'Ⱞ' => 'ⱞ',
'Ⱡ' => 'ⱡ',
'Ɫ' => 'ɫ',
'Ᵽ' => 'ᵽ',
'Ɽ' => 'ɽ',
'Ⱨ' => 'ⱨ',
'Ⱪ' => 'ⱪ',
'Ⱬ' => 'ⱬ',
'Ɑ' => 'ɑ',
'Ɱ' => 'ɱ',
'Ɐ' => 'ɐ',
'Ɒ' => 'ɒ',
'Ⱳ' => 'ⱳ',
'Ⱶ' => 'ⱶ',
'Ȿ' => 'ȿ',
'Ɀ' => 'ɀ',
'Ⲁ' => 'ⲁ',
'Ⲃ' => 'ⲃ',
'Ⲅ' => '',
'Ⲇ' => 'ⲇ',
'Ⲉ' => 'ⲉ',
'Ⲋ' => 'ⲋ',
'Ⲍ' => 'ⲍ',
'' => 'ⲏ',
'Ⲑ' => 'ⲑ',
'' => 'ⲓ',
'' => 'ⲕ',
'Ⲗ' => 'ⲗ',
'' => 'ⲙ',
'' => 'ⲛ',
'Ⲝ' => 'ⲝ',
'' => '',
'Ⲡ' => 'ⲡ',
'' => '',
'' => '',
'' => 'ⲧ',
'' => 'ⲩ',
'Ⲫ' => 'ⲫ',
'' => 'ⲭ',
'Ⲯ' => 'ⲯ',
'Ⲱ' => 'ⲱ',
'Ⲳ' => 'ⲳ',
'Ⲵ' => 'ⲵ',
'Ⲷ' => 'ⲷ',
'Ⲹ' => 'ⲹ',
'' => 'ⲻ',
'Ⲽ' => 'ⲽ',
'Ⲿ' => 'ⲿ',
'Ⳁ' => 'ⳁ',
'Ⳃ' => 'ⳃ',
'Ⳅ' => 'ⳅ',
'' => 'ⳇ',
'Ⳉ' => 'ⳉ',
'' => 'ⳋ',
'' => 'ⳍ',
'Ⳏ' => 'ⳏ',
'' => 'ⳑ',
'' => 'ⳓ',
'Ⳕ' => 'ⳕ',
'Ⳗ' => 'ⳗ',
'Ⳙ' => 'ⳙ',
'Ⳛ' => 'ⳛ',
'Ⳝ' => 'ⳝ',
'Ⳟ' => 'ⳟ',
'Ⳡ' => 'ⳡ',
'Ⳣ' => 'ⳣ',
'Ⳬ' => 'ⳬ',
'Ⳮ' => 'ⳮ',
'Ⳳ' => 'ⳳ',
'Ꙁ' => 'ꙁ',
'Ꙃ' => 'ꙃ',
'' => 'ꙅ',
'Ꙇ' => '',
'Ꙉ' => 'ꙉ',
'Ꙋ' => 'ꙋ',
'Ꙍ' => 'ꙍ',
'Ꙏ' => 'ꙏ',
'Ꙑ' => 'ꙑ',
'Ꙓ' => 'ꙓ',
'Ꙕ' => 'ꙕ',
'Ꙗ' => 'ꙗ',
'Ꙙ' => 'ꙙ',
'Ꙛ' => 'ꙛ',
'Ꙝ' => 'ꙝ',
'Ꙟ' => 'ꙟ',
'Ꙡ' => 'ꙡ',
'Ꙣ' => 'ꙣ',
'Ꙥ' => 'ꙥ',
'Ꙧ' => 'ꙧ',
'Ꙩ' => 'ꙩ',
'Ꙫ' => 'ꙫ',
'Ꙭ' => 'ꙭ',
'Ꚁ' => 'ꚁ',
'Ꚃ' => 'ꚃ',
'Ꚅ' => 'ꚅ',
'Ꚇ' => 'ꚇ',
'Ꚉ' => 'ꚉ',
'Ꚋ' => 'ꚋ',
'Ꚍ' => 'ꚍ',
'Ꚏ' => 'ꚏ',
'Ꚑ' => 'ꚑ',
'Ꚓ' => 'ꚓ',
'Ꚕ' => 'ꚕ',
'Ꚗ' => 'ꚗ',
'Ꚙ' => 'ꚙ',
'Ꚛ' => 'ꚛ',
'Ꜣ' => 'ꜣ',
'Ꜥ' => 'ꜥ',
'Ꜧ' => 'ꜧ',
'Ꜩ' => 'ꜩ',
'Ꜫ' => 'ꜫ',
'Ꜭ' => 'ꜭ',
'Ꜯ' => 'ꜯ',
'Ꜳ' => 'ꜳ',
'Ꜵ' => 'ꜵ',
'Ꜷ' => 'ꜷ',
'Ꜹ' => 'ꜹ',
'Ꜻ' => 'ꜻ',
'Ꜽ' => 'ꜽ',
'Ꜿ' => 'ꜿ',
'Ꝁ' => 'ꝁ',
'Ꝃ' => 'ꝃ',
'Ꝅ' => 'ꝅ',
'Ꝇ' => 'ꝇ',
'Ꝉ' => 'ꝉ',
'Ꝋ' => 'ꝋ',
'Ꝍ' => 'ꝍ',
'Ꝏ' => 'ꝏ',
'Ꝑ' => 'ꝑ',
'Ꝓ' => 'ꝓ',
'Ꝕ' => 'ꝕ',
'Ꝗ' => 'ꝗ',
'Ꝙ' => 'ꝙ',
'' => 'ꝛ',
'Ꝝ' => 'ꝝ',
'Ꝟ' => 'ꝟ',
'Ꝡ' => 'ꝡ',
'Ꝣ' => 'ꝣ',
'Ꝥ' => 'ꝥ',
'Ꝧ' => 'ꝧ',
'Ꝩ' => 'ꝩ',
'' => 'ꝫ',
'Ꝭ' => 'ꝭ',
'' => 'ꝯ',
'Ꝺ' => 'ꝺ',
'Ꝼ' => 'ꝼ',
'Ᵹ' => 'ᵹ',
'Ꝿ' => 'ꝿ',
'Ꞁ' => 'ꞁ',
'Ꞃ' => 'ꞃ',
'Ꞅ' => 'ꞅ',
'Ꞇ' => 'ꞇ',
'Ꞌ' => '',
'Ɥ' => 'ɥ',
'Ꞑ' => 'ꞑ',
'Ꞓ' => 'ꞓ',
'Ꞗ' => 'ꞗ',
'' => '',
'Ꞛ' => 'ꞛ',
'Ꞝ' => 'ꞝ',
'Ꞟ' => '',
'Ꞡ' => 'ꞡ',
'Ꞣ' => 'ꞣ',
'Ꞥ' => 'ꞥ',
'Ꞧ' => 'ꞧ',
'Ꞩ' => 'ꞩ',
'Ɦ' => 'ɦ',
'' => 'ɜ',
'Ɡ' => 'ɡ',
'Ɬ' => 'ɬ',
'Ʞ' => 'ʞ',
'Ʇ' => 'ʇ',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'𐐀' => '𐐨',
'𐐁' => '𐐩',
'𐐂' => '𐐪',
'𐐃' => '𐐫',
'𐐄' => '𐐬',
'𐐅' => '𐐭',
'𐐆' => '𐐮',
'𐐇' => '𐐯',
'𐐈' => '𐐰',
'𐐉' => '𐐱',
'𐐊' => '𐐲',
'𐐋' => '𐐳',
'𐐌' => '𐐴',
'𐐍' => '𐐵',
'𐐎' => '𐐶',
'𐐏' => '𐐷',
'𐐐' => '𐐸',
'𐐑' => '𐐹',
'𐐒' => '𐐺',
'𐐓' => '𐐻',
'𐐔' => '𐐼',
'𐐕' => '𐐽',
'𐐖' => '𐐾',
'𐐗' => '𐐿',
'𐐘' => '𐑀',
'𐐙' => '𐑁',
'𐐚' => '𐑂',
'𐐛' => '𐑃',
'𐐜' => '𐑄',
'𐐝' => '𐑅',
'𐐞' => '𐑆',
'𐐟' => '𐑇',
'𐐠' => '𐑈',
'𐐡' => '𐑉',
'𐐢' => '𐑊',
'𐐣' => '𐑋',
'𐐤' => '𐑌',
'𐐥' => '𐑍',
'𐐦' => '𐑎',
'𐐧' => '𐑏',
'𑢠' => '𑣀',
'𑢡' => '𑣁',
'𑢢' => '𑣂',
'𑢣' => '𑣃',
'𑢤' => '𑣄',
'𑢥' => '𑣅',
'𑢦' => '𑣆',
'𑢧' => '𑣇',
'𑢨' => '𑣈',
'𑢩' => '𑣉',
'𑢪' => '𑣊',
'𑢫' => '𑣋',
'𑢬' => '𑣌',
'𑢭' => '𑣍',
'𑢮' => '𑣎',
'𑢯' => '𑣏',
'𑢰' => '𑣐',
'𑢱' => '𑣑',
'𑢲' => '𑣒',
'𑢳' => '𑣓',
'𑢴' => '𑣔',
'𑢵' => '𑣕',
'𑢶' => '𑣖',
'𑢷' => '𑣗',
'𑢸' => '𑣘',
'𑢹' => '𑣙',
'𑢺' => '𑣚',
'𑢻' => '𑣛',
'𑢼' => '𑣜',
'𑢽' => '𑣝',
'𑢾' => '𑣞',
'𑢿' => '𑣟',
);
<?php
return array(
'a' => 'A',
'b' => 'B',
'c' => 'C',
'd' => 'D',
'e' => 'E',
'f' => 'F',
'g' => 'G',
'h' => 'H',
'i' => 'I',
'j' => 'J',
'k' => 'K',
'l' => 'L',
'm' => 'M',
'n' => 'N',
'o' => 'O',
'p' => 'P',
'q' => 'Q',
'r' => 'R',
's' => 'S',
't' => 'T',
'u' => 'U',
'v' => 'V',
'w' => 'W',
'x' => 'X',
'y' => 'Y',
'z' => 'Z',
'µ' => 'Μ',
'à' => 'À',
'á' => 'Á',
'â' => 'Â',
'ã' => 'Ã',
'ä' => 'Ä',
'å' => 'Å',
'æ' => 'Æ',
'ç' => 'Ç',
'è' => 'È',
'é' => 'É',
'ê' => 'Ê',
'ë' => 'Ë',
'ì' => 'Ì',
'í' => 'Í',
'î' => 'Î',
'ï' => 'Ï',
'ð' => 'Ð',
'ñ' => 'Ñ',
'ò' => 'Ò',
'ó' => 'Ó',
'ô' => 'Ô',
'õ' => 'Õ',
'ö' => 'Ö',
'ø' => 'Ø',
'ù' => 'Ù',
'ú' => 'Ú',
'û' => 'Û',
'ü' => 'Ü',
'ý' => 'Ý',
'þ' => 'Þ',
'ÿ' => 'Ÿ',
'ā' => 'Ā',
'ă' => 'Ă',
'ą' => 'Ą',
'ć' => 'Ć',
'ĉ' => 'Ĉ',
'ċ' => 'Ċ',
'č' => 'Č',
'ď' => 'Ď',
'đ' => 'Đ',
'ē' => 'Ē',
'ĕ' => 'Ĕ',
'ė' => 'Ė',
'ę' => 'Ę',
'ě' => 'Ě',
'ĝ' => 'Ĝ',
'ğ' => 'Ğ',
'ġ' => 'Ġ',
'ģ' => 'Ģ',
'ĥ' => 'Ĥ',
'ħ' => 'Ħ',
'ĩ' => 'Ĩ',
'ī' => 'Ī',
'ĭ' => 'Ĭ',
'į' => 'Į',
'ı' => 'I',
'ij' => 'IJ',
'ĵ' => 'Ĵ',
'ķ' => 'Ķ',
'ĺ' => 'Ĺ',
'ļ' => 'Ļ',
'ľ' => 'Ľ',
'ŀ' => 'Ŀ',
'ł' => 'Ł',
'ń' => 'Ń',
'ņ' => 'Ņ',
'ň' => 'Ň',
'ŋ' => 'Ŋ',
'ō' => 'Ō',
'ŏ' => 'Ŏ',
'ő' => 'Ő',
'œ' => 'Œ',
'ŕ' => 'Ŕ',
'ŗ' => 'Ŗ',
'ř' => 'Ř',
'ś' => 'Ś',
'ŝ' => 'Ŝ',
'ş' => 'Ş',
'š' => 'Š',
'ţ' => 'Ţ',
'ť' => 'Ť',
'ŧ' => 'Ŧ',
'ũ' => 'Ũ',
'ū' => 'Ū',
'ŭ' => 'Ŭ',
'ů' => 'Ů',
'ű' => 'Ű',
'ų' => 'Ų',
'ŵ' => 'Ŵ',
'ŷ' => 'Ŷ',
'ź' => 'Ź',
'ż' => 'Ż',
'ž' => 'Ž',
'ſ' => 'S',
'ƀ' => 'Ƀ',
'ƃ' => 'Ƃ',
'ƅ' => 'Ƅ',
'ƈ' => 'Ƈ',
'ƌ' => 'Ƌ',
'ƒ' => 'Ƒ',
'ƕ' => 'Ƕ',
'ƙ' => 'Ƙ',
'ƚ' => 'Ƚ',
'ƞ' => 'Ƞ',
'ơ' => 'Ơ',
'ƣ' => 'Ƣ',
'ƥ' => 'Ƥ',
'ƨ' => 'Ƨ',
'ƭ' => 'Ƭ',
'ư' => 'Ư',
'ƴ' => 'Ƴ',
'ƶ' => 'Ƶ',
'ƹ' => 'Ƹ',
'ƽ' => 'Ƽ',
'ƿ' => 'Ƿ',
'Dž' => 'DŽ',
'dž' => 'DŽ',
'Lj' => 'LJ',
'lj' => 'LJ',
'Nj' => 'NJ',
'nj' => 'NJ',
'ǎ' => 'Ǎ',
'ǐ' => 'Ǐ',
'ǒ' => 'Ǒ',
'ǔ' => 'Ǔ',
'ǖ' => 'Ǖ',
'ǘ' => 'Ǘ',
'ǚ' => 'Ǚ',
'ǜ' => 'Ǜ',
'ǝ' => 'Ǝ',
'ǟ' => 'Ǟ',
'ǡ' => 'Ǡ',
'ǣ' => 'Ǣ',
'ǥ' => 'Ǥ',
'ǧ' => 'Ǧ',
'ǩ' => 'Ǩ',
'ǫ' => 'Ǫ',
'ǭ' => 'Ǭ',
'ǯ' => 'Ǯ',
'Dz' => 'DZ',
'dz' => 'DZ',
'ǵ' => 'Ǵ',
'ǹ' => 'Ǹ',
'ǻ' => 'Ǻ',
'ǽ' => 'Ǽ',
'ǿ' => 'Ǿ',
'ȁ' => 'Ȁ',
'ȃ' => 'Ȃ',
'ȅ' => 'Ȅ',
'ȇ' => 'Ȇ',
'ȉ' => 'Ȉ',
'ȋ' => 'Ȋ',
'ȍ' => 'Ȍ',
'ȏ' => 'Ȏ',
'ȑ' => 'Ȑ',
'ȓ' => 'Ȓ',
'ȕ' => 'Ȕ',
'ȗ' => 'Ȗ',
'ș' => 'Ș',
'ț' => 'Ț',
'ȝ' => 'Ȝ',
'ȟ' => 'Ȟ',
'ȣ' => 'Ȣ',
'ȥ' => 'Ȥ',
'ȧ' => 'Ȧ',
'ȩ' => 'Ȩ',
'ȫ' => 'Ȫ',
'ȭ' => 'Ȭ',
'ȯ' => 'Ȯ',
'ȱ' => 'Ȱ',
'ȳ' => 'Ȳ',
'ȼ' => 'Ȼ',
'ȿ' => 'Ȿ',
'ɀ' => 'Ɀ',
'ɂ' => 'Ɂ',
'ɇ' => 'Ɇ',
'ɉ' => 'Ɉ',
'ɋ' => 'Ɋ',
'ɍ' => 'Ɍ',
'ɏ' => 'Ɏ',
'ɐ' => 'Ɐ',
'ɑ' => 'Ɑ',
'ɒ' => 'Ɒ',
'ɓ' => 'Ɓ',
'ɔ' => 'Ɔ',
'ɖ' => 'Ɖ',
'ɗ' => 'Ɗ',
'ə' => 'Ə',
'ɛ' => 'Ɛ',
'ɜ' => '',
'ɠ' => 'Ɠ',
'ɡ' => 'Ɡ',
'ɣ' => 'Ɣ',
'ɥ' => 'Ɥ',
'ɦ' => 'Ɦ',
'ɨ' => 'Ɨ',
'ɩ' => 'Ɩ',
'ɫ' => 'Ɫ',
'ɬ' => 'Ɬ',
'ɯ' => 'Ɯ',
'ɱ' => 'Ɱ',
'ɲ' => 'Ɲ',
'ɵ' => 'Ɵ',
'ɽ' => 'Ɽ',
'ʀ' => 'Ʀ',
'ʃ' => 'Ʃ',
'ʇ' => 'Ʇ',
'ʈ' => 'Ʈ',
'ʉ' => 'Ʉ',
'ʊ' => 'Ʊ',
'ʋ' => 'Ʋ',
'ʌ' => 'Ʌ',
'ʒ' => 'Ʒ',
'ʞ' => 'Ʞ',
'ͅ' => 'Ι',
'ͱ' => 'Ͱ',
'ͳ' => 'Ͳ',
'ͷ' => 'Ͷ',
'ͻ' => 'Ͻ',
'ͼ' => 'Ͼ',
'ͽ' => 'Ͽ',
'ά' => 'Ά',
'έ' => 'Έ',
'ή' => 'Ή',
'ί' => 'Ί',
'α' => 'Α',
'β' => 'Β',
'γ' => 'Γ',
'δ' => 'Δ',
'ε' => 'Ε',
'ζ' => 'Ζ',
'η' => 'Η',
'θ' => 'Θ',
'ι' => 'Ι',
'κ' => 'Κ',
'λ' => 'Λ',
'μ' => 'Μ',
'ν' => 'Ν',
'ξ' => 'Ξ',
'ο' => 'Ο',
'π' => 'Π',
'ρ' => 'Ρ',
'ς' => 'Σ',
'σ' => 'Σ',
'τ' => 'Τ',
'υ' => 'Υ',
'φ' => 'Φ',
'χ' => 'Χ',
'ψ' => 'Ψ',
'ω' => 'Ω',
'ϊ' => 'Ϊ',
'ϋ' => 'Ϋ',
'ό' => 'Ό',
'ύ' => 'Ύ',
'ώ' => 'Ώ',
'ϐ' => 'Β',
'ϑ' => 'Θ',
'ϕ' => 'Φ',
'ϖ' => 'Π',
'ϗ' => 'Ϗ',
'ϙ' => 'Ϙ',
'ϛ' => 'Ϛ',
'ϝ' => 'Ϝ',
'ϟ' => 'Ϟ',
'ϡ' => 'Ϡ',
'ϣ' => 'Ϣ',
'ϥ' => 'Ϥ',
'ϧ' => 'Ϧ',
'ϩ' => 'Ϩ',
'ϫ' => 'Ϫ',
'ϭ' => 'Ϭ',
'ϯ' => 'Ϯ',
'ϰ' => 'Κ',
'ϱ' => 'Ρ',
'ϲ' => 'Ϲ',
'ϳ' => 'Ϳ',
'ϵ' => 'Ε',
'ϸ' => 'Ϸ',
'ϻ' => 'Ϻ',
'а' => 'А',
'б' => 'Б',
'в' => 'В',
'г' => 'Г',
'д' => 'Д',
'е' => 'Е',
'ж' => 'Ж',
'з' => 'З',
'и' => 'И',
'й' => 'Й',
'к' => 'К',
'л' => 'Л',
'м' => 'М',
'н' => 'Н',
'о' => 'О',
'п' => 'П',
'р' => 'Р',
'с' => 'С',
'т' => 'Т',
'у' => 'У',
'ф' => 'Ф',
'х' => 'Х',
'ц' => 'Ц',
'ч' => 'Ч',
'ш' => 'Ш',
'щ' => 'Щ',
'ъ' => 'Ъ',
'ы' => 'Ы',
'ь' => 'Ь',
'э' => 'Э',
'ю' => 'Ю',
'я' => 'Я',
'ѐ' => 'Ѐ',
'ё' => 'Ё',
'ђ' => 'Ђ',
'ѓ' => 'Ѓ',
'є' => 'Є',
'ѕ' => 'Ѕ',
'і' => 'І',
'ї' => 'Ї',
'ј' => 'Ј',
'љ' => 'Љ',
'њ' => 'Њ',
'ћ' => 'Ћ',
'ќ' => 'Ќ',
'ѝ' => 'Ѝ',
'ў' => 'Ў',
'џ' => 'Џ',
'ѡ' => 'Ѡ',
'ѣ' => 'Ѣ',
'ѥ' => 'Ѥ',
'ѧ' => 'Ѧ',
'ѩ' => 'Ѩ',
'ѫ' => 'Ѫ',
'ѭ' => 'Ѭ',
'ѯ' => 'Ѯ',
'ѱ' => 'Ѱ',
'ѳ' => 'Ѳ',
'ѵ' => 'Ѵ',
'ѷ' => 'Ѷ',
'ѹ' => 'Ѹ',
'ѻ' => 'Ѻ',
'ѽ' => 'Ѽ',
'ѿ' => 'Ѿ',
'ҁ' => 'Ҁ',
'ҋ' => 'Ҋ',
'ҍ' => 'Ҍ',
'ҏ' => 'Ҏ',
'ґ' => 'Ґ',
'ғ' => 'Ғ',
'ҕ' => 'Ҕ',
'җ' => 'Җ',
'ҙ' => 'Ҙ',
'қ' => 'Қ',
'ҝ' => 'Ҝ',
'ҟ' => 'Ҟ',
'ҡ' => 'Ҡ',
'ң' => 'Ң',
'ҥ' => 'Ҥ',
'ҧ' => 'Ҧ',
'ҩ' => 'Ҩ',
'ҫ' => 'Ҫ',
'ҭ' => 'Ҭ',
'ү' => 'Ү',
'ұ' => 'Ұ',
'ҳ' => 'Ҳ',
'ҵ' => 'Ҵ',
'ҷ' => 'Ҷ',
'ҹ' => 'Ҹ',
'һ' => 'Һ',
'ҽ' => 'Ҽ',
'ҿ' => 'Ҿ',
'ӂ' => 'Ӂ',
'ӄ' => 'Ӄ',
'ӆ' => 'Ӆ',
'ӈ' => 'Ӈ',
'ӊ' => 'Ӊ',
'ӌ' => 'Ӌ',
'ӎ' => 'Ӎ',
'ӏ' => 'Ӏ',
'ӑ' => 'Ӑ',
'ӓ' => 'Ӓ',
'ӕ' => 'Ӕ',
'ӗ' => 'Ӗ',
'ә' => 'Ә',
'ӛ' => 'Ӛ',
'ӝ' => 'Ӝ',
'ӟ' => 'Ӟ',
'ӡ' => 'Ӡ',
'ӣ' => 'Ӣ',
'ӥ' => 'Ӥ',
'ӧ' => 'Ӧ',
'ө' => 'Ө',
'ӫ' => 'Ӫ',
'ӭ' => 'Ӭ',
'ӯ' => 'Ӯ',
'ӱ' => 'Ӱ',
'ӳ' => 'Ӳ',
'ӵ' => 'Ӵ',
'ӷ' => 'Ӷ',
'ӹ' => 'Ӹ',
'ӻ' => 'Ӻ',
'ӽ' => 'Ӽ',
'ӿ' => 'Ӿ',
'ԁ' => 'Ԁ',
'ԃ' => 'Ԃ',
'ԅ' => 'Ԅ',
'ԇ' => 'Ԇ',
'ԉ' => 'Ԉ',
'ԋ' => 'Ԋ',
'ԍ' => 'Ԍ',
'ԏ' => 'Ԏ',
'ԑ' => 'Ԑ',
'ԓ' => 'Ԓ',
'ԕ' => 'Ԕ',
'ԗ' => 'Ԗ',
'ԙ' => 'Ԙ',
'ԛ' => 'Ԛ',
'ԝ' => 'Ԝ',
'ԟ' => 'Ԟ',
'ԡ' => 'Ԡ',
'ԣ' => 'Ԣ',
'ԥ' => 'Ԥ',
'ԧ' => 'Ԧ',
'ԩ' => 'Ԩ',
'ԫ' => 'Ԫ',
'ԭ' => 'Ԭ',
'ԯ' => 'Ԯ',
'ա' => 'Ա',
'բ' => 'Բ',
'գ' => 'Գ',
'դ' => 'Դ',
'ե' => 'Ե',
'զ' => 'Զ',
'է' => 'Է',
'ը' => 'Ը',
'թ' => 'Թ',
'ժ' => 'Ժ',
'ի' => 'Ի',
'լ' => 'Լ',
'խ' => 'Խ',
'ծ' => 'Ծ',
'կ' => 'Կ',
'հ' => 'Հ',
'ձ' => 'Ձ',
'ղ' => 'Ղ',
'ճ' => 'Ճ',
'մ' => 'Մ',
'յ' => 'Յ',
'ն' => 'Ն',
'շ' => 'Շ',
'ո' => 'Ո',
'չ' => 'Չ',
'պ' => 'Պ',
'ջ' => 'Ջ',
'ռ' => 'Ռ',
'ս' => 'Ս',
'վ' => 'Վ',
'տ' => 'Տ',
'ր' => 'Ր',
'ց' => 'Ց',
'ւ' => 'Ւ',
'փ' => 'Փ',
'ք' => 'Ք',
'օ' => 'Օ',
'ֆ' => 'Ֆ',
'ᵹ' => 'Ᵹ',
'ᵽ' => 'Ᵽ',
'ḁ' => 'Ḁ',
'ḃ' => 'Ḃ',
'ḅ' => 'Ḅ',
'ḇ' => 'Ḇ',
'ḉ' => 'Ḉ',
'ḋ' => 'Ḋ',
'ḍ' => 'Ḍ',
'ḏ' => 'Ḏ',
'ḑ' => 'Ḑ',
'ḓ' => 'Ḓ',
'ḕ' => 'Ḕ',
'ḗ' => 'Ḗ',
'ḙ' => 'Ḙ',
'ḛ' => 'Ḛ',
'ḝ' => 'Ḝ',
'ḟ' => 'Ḟ',
'ḡ' => 'Ḡ',
'ḣ' => 'Ḣ',
'ḥ' => 'Ḥ',
'ḧ' => 'Ḧ',
'ḩ' => 'Ḩ',
'ḫ' => 'Ḫ',
'ḭ' => 'Ḭ',
'ḯ' => 'Ḯ',
'ḱ' => 'Ḱ',
'ḳ' => 'Ḳ',
'ḵ' => 'Ḵ',
'ḷ' => 'Ḷ',
'ḹ' => 'Ḹ',
'ḻ' => 'Ḻ',
'ḽ' => 'Ḽ',
'ḿ' => 'Ḿ',
'ṁ' => 'Ṁ',
'ṃ' => 'Ṃ',
'ṅ' => 'Ṅ',
'ṇ' => 'Ṇ',
'ṉ' => 'Ṉ',
'ṋ' => 'Ṋ',
'ṍ' => 'Ṍ',
'ṏ' => 'Ṏ',
'ṑ' => 'Ṑ',
'ṓ' => 'Ṓ',
'ṕ' => 'Ṕ',
'ṗ' => 'Ṗ',
'ṙ' => 'Ṙ',
'ṛ' => 'Ṛ',
'ṝ' => 'Ṝ',
'ṟ' => 'Ṟ',
'ṡ' => 'Ṡ',
'ṣ' => 'Ṣ',
'ṥ' => 'Ṥ',
'ṧ' => 'Ṧ',
'ṩ' => 'Ṩ',
'ṫ' => 'Ṫ',
'ṭ' => 'Ṭ',
'ṯ' => 'Ṯ',
'ṱ' => 'Ṱ',
'ṳ' => 'Ṳ',
'ṵ' => 'Ṵ',
'ṷ' => 'Ṷ',
'ṹ' => 'Ṹ',
'ṻ' => 'Ṻ',
'ṽ' => 'Ṽ',
'ṿ' => 'Ṿ',
'ẁ' => 'Ẁ',
'ẃ' => 'Ẃ',
'ẅ' => 'Ẅ',
'ẇ' => 'Ẇ',
'ẉ' => 'Ẉ',
'ẋ' => 'Ẋ',
'ẍ' => 'Ẍ',
'ẏ' => 'Ẏ',
'ẑ' => 'Ẑ',
'ẓ' => 'Ẓ',
'ẕ' => 'Ẕ',
'ẛ' => 'Ṡ',
'ạ' => 'Ạ',
'ả' => 'Ả',
'ấ' => 'Ấ',
'ầ' => 'Ầ',
'ẩ' => 'Ẩ',
'ẫ' => 'Ẫ',
'ậ' => 'Ậ',
'ắ' => 'Ắ',
'ằ' => 'Ằ',
'ẳ' => 'Ẳ',
'ẵ' => 'Ẵ',
'ặ' => 'Ặ',
'ẹ' => 'Ẹ',
'ẻ' => 'Ẻ',
'ẽ' => 'Ẽ',
'ế' => 'Ế',
'ề' => 'Ề',
'ể' => 'Ể',
'ễ' => 'Ễ',
'ệ' => 'Ệ',
'ỉ' => 'Ỉ',
'ị' => 'Ị',
'ọ' => 'Ọ',
'ỏ' => 'Ỏ',
'ố' => 'Ố',
'ồ' => 'Ồ',
'ổ' => 'Ổ',
'ỗ' => 'Ỗ',
'ộ' => 'Ộ',
'ớ' => 'Ớ',
'ờ' => 'Ờ',
'ở' => 'Ở',
'ỡ' => 'Ỡ',
'ợ' => 'Ợ',
'ụ' => 'Ụ',
'ủ' => 'Ủ',
'ứ' => 'Ứ',
'ừ' => 'Ừ',
'ử' => 'Ử',
'ữ' => 'Ữ',
'ự' => 'Ự',
'ỳ' => 'Ỳ',
'ỵ' => 'Ỵ',
'ỷ' => 'Ỷ',
'ỹ' => 'Ỹ',
'ỻ' => 'Ỻ',
'ỽ' => 'Ỽ',
'ỿ' => 'Ỿ',
'ἀ' => 'Ἀ',
'ἁ' => 'Ἁ',
'ἂ' => 'Ἂ',
'ἃ' => 'Ἃ',
'ἄ' => 'Ἄ',
'ἅ' => 'Ἅ',
'ἆ' => 'Ἆ',
'ἇ' => 'Ἇ',
'ἐ' => 'Ἐ',
'ἑ' => 'Ἑ',
'ἒ' => 'Ἒ',
'ἓ' => 'Ἓ',
'ἔ' => 'Ἔ',
'ἕ' => 'Ἕ',
'ἠ' => 'Ἠ',
'ἡ' => 'Ἡ',
'ἢ' => 'Ἢ',
'ἣ' => 'Ἣ',
'ἤ' => 'Ἤ',
'ἥ' => 'Ἥ',
'ἦ' => 'Ἦ',
'ἧ' => 'Ἧ',
'ἰ' => 'Ἰ',
'ἱ' => 'Ἱ',
'ἲ' => 'Ἲ',
'ἳ' => 'Ἳ',
'ἴ' => 'Ἴ',
'ἵ' => 'Ἵ',
'ἶ' => 'Ἶ',
'ἷ' => 'Ἷ',
'ὀ' => 'Ὀ',
'ὁ' => 'Ὁ',
'ὂ' => 'Ὂ',
'ὃ' => 'Ὃ',
'ὄ' => 'Ὄ',
'ὅ' => 'Ὅ',
'ὑ' => 'Ὑ',
'ὓ' => 'Ὓ',
'ὕ' => 'Ὕ',
'ὗ' => 'Ὗ',
'ὠ' => 'Ὠ',
'ὡ' => 'Ὡ',
'ὢ' => 'Ὢ',
'ὣ' => 'Ὣ',
'ὤ' => 'Ὤ',
'ὥ' => 'Ὥ',
'ὦ' => 'Ὦ',
'ὧ' => 'Ὧ',
'ὰ' => 'Ὰ',
'ά' => 'Ά',
'ὲ' => 'Ὲ',
'έ' => 'Έ',
'ὴ' => 'Ὴ',
'ή' => 'Ή',
'ὶ' => 'Ὶ',
'ί' => 'Ί',
'ὸ' => 'Ὸ',
'ό' => 'Ό',
'ὺ' => 'Ὺ',
'ύ' => 'Ύ',
'ὼ' => 'Ὼ',
'ώ' => 'Ώ',
'ᾀ' => 'ᾈ',
'ᾁ' => 'ᾉ',
'ᾂ' => 'ᾊ',
'ᾃ' => 'ᾋ',
'ᾄ' => 'ᾌ',
'ᾅ' => 'ᾍ',
'ᾆ' => 'ᾎ',
'ᾇ' => 'ᾏ',
'ᾐ' => 'ᾘ',
'ᾑ' => 'ᾙ',
'ᾒ' => 'ᾚ',
'ᾓ' => 'ᾛ',
'ᾔ' => 'ᾜ',
'ᾕ' => 'ᾝ',
'ᾖ' => 'ᾞ',
'ᾗ' => 'ᾟ',
'ᾠ' => 'ᾨ',
'ᾡ' => 'ᾩ',
'ᾢ' => 'ᾪ',
'ᾣ' => 'ᾫ',
'ᾤ' => 'ᾬ',
'ᾥ' => 'ᾭ',
'ᾦ' => 'ᾮ',
'ᾧ' => 'ᾯ',
'ᾰ' => 'Ᾰ',
'ᾱ' => 'Ᾱ',
'ᾳ' => 'ᾼ',
'' => 'Ι',
'ῃ' => 'ῌ',
'ῐ' => 'Ῐ',
'ῑ' => 'Ῑ',
'ῠ' => 'Ῠ',
'ῡ' => 'Ῡ',
'ῥ' => 'Ῥ',
'ῳ' => 'ῼ',
'ⅎ' => 'Ⅎ',
'' => '',
'ⅱ' => 'Ⅱ',
'ⅲ' => 'Ⅲ',
'ⅳ' => 'Ⅳ',
'' => '',
'ⅵ' => 'Ⅵ',
'ⅶ' => 'Ⅶ',
'ⅷ' => 'Ⅷ',
'ⅸ' => 'Ⅸ',
'' => '',
'ⅺ' => 'Ⅺ',
'ⅻ' => 'Ⅻ',
'' => '',
'' => '',
'' => '',
'ⅿ' => '',
'ↄ' => 'Ↄ',
'ⓐ' => 'Ⓐ',
'ⓑ' => 'Ⓑ',
'ⓒ' => 'Ⓒ',
'ⓓ' => 'Ⓓ',
'ⓔ' => 'Ⓔ',
'ⓕ' => 'Ⓕ',
'ⓖ' => 'Ⓖ',
'ⓗ' => 'Ⓗ',
'ⓘ' => 'Ⓘ',
'ⓙ' => 'Ⓙ',
'ⓚ' => 'Ⓚ',
'ⓛ' => 'Ⓛ',
'ⓜ' => 'Ⓜ',
'ⓝ' => 'Ⓝ',
'ⓞ' => 'Ⓞ',
'ⓟ' => 'Ⓟ',
'ⓠ' => 'Ⓠ',
'ⓡ' => 'Ⓡ',
'ⓢ' => 'Ⓢ',
'ⓣ' => 'Ⓣ',
'ⓤ' => 'Ⓤ',
'ⓥ' => 'Ⓥ',
'ⓦ' => 'Ⓦ',
'ⓧ' => 'Ⓧ',
'ⓨ' => 'Ⓨ',
'ⓩ' => 'Ⓩ',
'ⰰ' => 'Ⰰ',
'ⰱ' => 'Ⰱ',
'ⰲ' => 'Ⰲ',
'ⰳ' => 'Ⰳ',
'ⰴ' => 'Ⰴ',
'ⰵ' => 'Ⰵ',
'ⰶ' => 'Ⰶ',
'ⰷ' => 'Ⰷ',
'ⰸ' => 'Ⰸ',
'ⰹ' => 'Ⰹ',
'ⰺ' => 'Ⰺ',
'ⰻ' => 'Ⰻ',
'ⰼ' => 'Ⰼ',
'ⰽ' => 'Ⰽ',
'ⰾ' => 'Ⰾ',
'ⰿ' => 'Ⰿ',
'ⱀ' => 'Ⱀ',
'ⱁ' => 'Ⱁ',
'ⱂ' => 'Ⱂ',
'ⱃ' => 'Ⱃ',
'ⱄ' => 'Ⱄ',
'ⱅ' => 'Ⱅ',
'ⱆ' => 'Ⱆ',
'ⱇ' => 'Ⱇ',
'ⱈ' => 'Ⱈ',
'ⱉ' => 'Ⱉ',
'ⱊ' => 'Ⱊ',
'ⱋ' => 'Ⱋ',
'ⱌ' => 'Ⱌ',
'ⱍ' => 'Ⱍ',
'ⱎ' => 'Ⱎ',
'ⱏ' => 'Ⱏ',
'ⱐ' => 'Ⱐ',
'ⱑ' => 'Ⱑ',
'ⱒ' => 'Ⱒ',
'ⱓ' => 'Ⱓ',
'ⱔ' => 'Ⱔ',
'ⱕ' => 'Ⱕ',
'ⱖ' => 'Ⱖ',
'ⱗ' => 'Ⱗ',
'ⱘ' => 'Ⱘ',
'ⱙ' => 'Ⱙ',
'ⱚ' => 'Ⱚ',
'ⱛ' => 'Ⱛ',
'ⱜ' => 'Ⱜ',
'ⱝ' => 'Ⱝ',
'ⱞ' => 'Ⱞ',
'ⱡ' => 'Ⱡ',
'ⱥ' => 'Ⱥ',
'ⱦ' => 'Ⱦ',
'ⱨ' => 'Ⱨ',
'ⱪ' => 'Ⱪ',
'ⱬ' => 'Ⱬ',
'ⱳ' => 'Ⱳ',
'ⱶ' => 'Ⱶ',
'ⲁ' => 'Ⲁ',
'ⲃ' => 'Ⲃ',
'' => 'Ⲅ',
'ⲇ' => 'Ⲇ',
'ⲉ' => 'Ⲉ',
'ⲋ' => 'Ⲋ',
'ⲍ' => 'Ⲍ',
'ⲏ' => '',
'ⲑ' => 'Ⲑ',
'ⲓ' => '',
'ⲕ' => '',
'ⲗ' => 'Ⲗ',
'ⲙ' => '',
'ⲛ' => '',
'ⲝ' => 'Ⲝ',
'' => '',
'ⲡ' => 'Ⲡ',
'' => '',
'' => '',
'ⲧ' => '',
'ⲩ' => '',
'ⲫ' => 'Ⲫ',
'ⲭ' => '',
'ⲯ' => 'Ⲯ',
'ⲱ' => 'Ⲱ',
'ⲳ' => 'Ⲳ',
'ⲵ' => 'Ⲵ',
'ⲷ' => 'Ⲷ',
'ⲹ' => 'Ⲹ',
'ⲻ' => '',
'ⲽ' => 'Ⲽ',
'ⲿ' => 'Ⲿ',
'ⳁ' => 'Ⳁ',
'ⳃ' => 'Ⳃ',
'ⳅ' => 'Ⳅ',
'ⳇ' => '',
'ⳉ' => 'Ⳉ',
'ⳋ' => '',
'ⳍ' => '',
'ⳏ' => 'Ⳏ',
'ⳑ' => '',
'ⳓ' => '',
'ⳕ' => 'Ⳕ',
'ⳗ' => 'Ⳗ',
'ⳙ' => 'Ⳙ',
'ⳛ' => 'Ⳛ',
'ⳝ' => 'Ⳝ',
'ⳟ' => 'Ⳟ',
'ⳡ' => 'Ⳡ',
'ⳣ' => 'Ⳣ',
'ⳬ' => 'Ⳬ',
'ⳮ' => 'Ⳮ',
'ⳳ' => 'Ⳳ',
'ⴀ' => 'Ⴀ',
'ⴁ' => 'Ⴁ',
'ⴂ' => 'Ⴂ',
'ⴃ' => 'Ⴃ',
'ⴄ' => 'Ⴄ',
'ⴅ' => 'Ⴅ',
'ⴆ' => 'Ⴆ',
'ⴇ' => 'Ⴇ',
'ⴈ' => 'Ⴈ',
'ⴉ' => 'Ⴉ',
'ⴊ' => 'Ⴊ',
'ⴋ' => 'Ⴋ',
'ⴌ' => 'Ⴌ',
'ⴍ' => 'Ⴍ',
'ⴎ' => 'Ⴎ',
'ⴏ' => 'Ⴏ',
'ⴐ' => 'Ⴐ',
'ⴑ' => 'Ⴑ',
'ⴒ' => 'Ⴒ',
'ⴓ' => 'Ⴓ',
'ⴔ' => 'Ⴔ',
'ⴕ' => 'Ⴕ',
'ⴖ' => 'Ⴖ',
'ⴗ' => 'Ⴗ',
'ⴘ' => 'Ⴘ',
'ⴙ' => 'Ⴙ',
'ⴚ' => 'Ⴚ',
'ⴛ' => 'Ⴛ',
'ⴜ' => 'Ⴜ',
'ⴝ' => 'Ⴝ',
'ⴞ' => 'Ⴞ',
'ⴟ' => 'Ⴟ',
'ⴠ' => 'Ⴠ',
'ⴡ' => 'Ⴡ',
'ⴢ' => 'Ⴢ',
'ⴣ' => 'Ⴣ',
'ⴤ' => 'Ⴤ',
'ⴥ' => 'Ⴥ',
'ⴧ' => 'Ⴧ',
'ⴭ' => 'Ⴭ',
'ꙁ' => 'Ꙁ',
'ꙃ' => 'Ꙃ',
'ꙅ' => '',
'' => 'Ꙇ',
'ꙉ' => 'Ꙉ',
'ꙋ' => 'Ꙋ',
'ꙍ' => 'Ꙍ',
'ꙏ' => 'Ꙏ',
'ꙑ' => 'Ꙑ',
'ꙓ' => 'Ꙓ',
'ꙕ' => 'Ꙕ',
'ꙗ' => 'Ꙗ',
'ꙙ' => 'Ꙙ',
'ꙛ' => 'Ꙛ',
'ꙝ' => 'Ꙝ',
'ꙟ' => 'Ꙟ',
'ꙡ' => 'Ꙡ',
'ꙣ' => 'Ꙣ',
'ꙥ' => 'Ꙥ',
'ꙧ' => 'Ꙧ',
'ꙩ' => 'Ꙩ',
'ꙫ' => 'Ꙫ',
'ꙭ' => 'Ꙭ',
'ꚁ' => 'Ꚁ',
'ꚃ' => 'Ꚃ',
'ꚅ' => 'Ꚅ',
'ꚇ' => 'Ꚇ',
'ꚉ' => 'Ꚉ',
'ꚋ' => 'Ꚋ',
'ꚍ' => 'Ꚍ',
'ꚏ' => 'Ꚏ',
'ꚑ' => 'Ꚑ',
'ꚓ' => 'Ꚓ',
'ꚕ' => 'Ꚕ',
'ꚗ' => 'Ꚗ',
'ꚙ' => 'Ꚙ',
'ꚛ' => 'Ꚛ',
'ꜣ' => 'Ꜣ',
'ꜥ' => 'Ꜥ',
'ꜧ' => 'Ꜧ',
'ꜩ' => 'Ꜩ',
'ꜫ' => 'Ꜫ',
'ꜭ' => 'Ꜭ',
'ꜯ' => 'Ꜯ',
'ꜳ' => 'Ꜳ',
'ꜵ' => 'Ꜵ',
'ꜷ' => 'Ꜷ',
'ꜹ' => 'Ꜹ',
'ꜻ' => 'Ꜻ',
'ꜽ' => 'Ꜽ',
'ꜿ' => 'Ꜿ',
'ꝁ' => 'Ꝁ',
'ꝃ' => 'Ꝃ',
'ꝅ' => 'Ꝅ',
'ꝇ' => 'Ꝇ',
'ꝉ' => 'Ꝉ',
'ꝋ' => 'Ꝋ',
'ꝍ' => 'Ꝍ',
'ꝏ' => 'Ꝏ',
'ꝑ' => 'Ꝑ',
'ꝓ' => 'Ꝓ',
'ꝕ' => 'Ꝕ',
'ꝗ' => 'Ꝗ',
'ꝙ' => 'Ꝙ',
'ꝛ' => '',
'ꝝ' => 'Ꝝ',
'ꝟ' => 'Ꝟ',
'ꝡ' => 'Ꝡ',
'ꝣ' => 'Ꝣ',
'ꝥ' => 'Ꝥ',
'ꝧ' => 'Ꝧ',
'ꝩ' => 'Ꝩ',
'ꝫ' => '',
'ꝭ' => 'Ꝭ',
'ꝯ' => '',
'ꝺ' => 'Ꝺ',
'ꝼ' => 'Ꝼ',
'ꝿ' => 'Ꝿ',
'ꞁ' => 'Ꞁ',
'ꞃ' => 'Ꞃ',
'ꞅ' => 'Ꞅ',
'ꞇ' => 'Ꞇ',
'' => 'Ꞌ',
'ꞑ' => 'Ꞑ',
'ꞓ' => 'Ꞓ',
'ꞗ' => 'Ꞗ',
'' => '',
'ꞛ' => 'Ꞛ',
'ꞝ' => 'Ꞝ',
'' => 'Ꞟ',
'ꞡ' => 'Ꞡ',
'ꞣ' => 'Ꞣ',
'ꞥ' => 'Ꞥ',
'ꞧ' => 'Ꞧ',
'ꞩ' => 'Ꞩ',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'' => '',
'𐐨' => '𐐀',
'𐐩' => '𐐁',
'𐐪' => '𐐂',
'𐐫' => '𐐃',
'𐐬' => '𐐄',
'𐐭' => '𐐅',
'𐐮' => '𐐆',
'𐐯' => '𐐇',
'𐐰' => '𐐈',
'𐐱' => '𐐉',
'𐐲' => '𐐊',
'𐐳' => '𐐋',
'𐐴' => '𐐌',
'𐐵' => '𐐍',
'𐐶' => '𐐎',
'𐐷' => '𐐏',
'𐐸' => '𐐐',
'𐐹' => '𐐑',
'𐐺' => '𐐒',
'𐐻' => '𐐓',
'𐐼' => '𐐔',
'𐐽' => '𐐕',
'𐐾' => '𐐖',
'𐐿' => '𐐗',
'𐑀' => '𐐘',
'𐑁' => '𐐙',
'𐑂' => '𐐚',
'𐑃' => '𐐛',
'𐑄' => '𐐜',
'𐑅' => '𐐝',
'𐑆' => '𐐞',
'𐑇' => '𐐟',
'𐑈' => '𐐠',
'𐑉' => '𐐡',
'𐑊' => '𐐢',
'𐑋' => '𐐣',
'𐑌' => '𐐤',
'𐑍' => '𐐥',
'𐑎' => '𐐦',
'𐑏' => '𐐧',
'𑣀' => '𑢠',
'𑣁' => '𑢡',
'𑣂' => '𑢢',
'𑣃' => '𑢣',
'𑣄' => '𑢤',
'𑣅' => '𑢥',
'𑣆' => '𑢦',
'𑣇' => '𑢧',
'𑣈' => '𑢨',
'𑣉' => '𑢩',
'𑣊' => '𑢪',
'𑣋' => '𑢫',
'𑣌' => '𑢬',
'𑣍' => '𑢭',
'𑣎' => '𑢮',
'𑣏' => '𑢯',
'𑣐' => '𑢰',
'𑣑' => '𑢱',
'𑣒' => '𑢲',
'𑣓' => '𑢳',
'𑣔' => '𑢴',
'𑣕' => '𑢵',
'𑣖' => '𑢶',
'𑣗' => '𑢷',
'𑣘' => '𑢸',
'𑣙' => '𑢹',
'𑣚' => '𑢺',
'𑣛' => '𑢻',
'𑣜' => '𑢼',
'𑣝' => '𑢽',
'𑣞' => '𑢾',
'𑣟' => '𑢿',
);
<?php
// from Case_Ignorable in https://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt
return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u';
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Mbstring as p;
if (!function_exists('mb_strlen')) {
define('MB_CASE_UPPER', 0);
define('MB_CASE_LOWER', 1);
define('MB_CASE_TITLE', 2);
function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); }
function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); }
function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); }
function mb_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); }
function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); }
function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); }
function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); }
function mb_language($lang = null) { return p\Mbstring::mb_language($lang); }
function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); }
function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); }
function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); }
function mb_parse_str($s, &$result = array()) { parse_str($s, $result); }
function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); }
function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); }
function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); }
function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); }
function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); }
function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); }
function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); }
function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); }
function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); }
function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); }
function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); }
function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); }
function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); }
function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); }
function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); }
function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); }
function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); }
function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); }
function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); }
}
if (!function_exists('mb_chr')) {
function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); }
function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); }
function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); }
}
if (!function_exists('mb_str_split')) {
function mb_str_split($string, $split_length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $split_length, $encoding); }
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Mbstring;
/**
* Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
*
* Implemented:
* - mb_chr - Returns a specific character from its Unicode code point
* - mb_convert_encoding - Convert character encoding
* - mb_convert_variables - Convert character code in variable(s)
* - mb_decode_mimeheader - Decode string in MIME header field
* - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
* - mb_decode_numericentity - Decode HTML numeric string reference to character
* - mb_encode_numericentity - Encode character to HTML numeric string reference
* - mb_convert_case - Perform case folding on a string
* - mb_detect_encoding - Detect character encoding
* - mb_get_info - Get internal settings of mbstring
* - mb_http_input - Detect HTTP input character encoding
* - mb_http_output - Set/Get HTTP output character encoding
* - mb_internal_encoding - Set/Get internal character encoding
* - mb_list_encodings - Returns an array of all supported encodings
* - mb_ord - Returns the Unicode code point of a character
* - mb_output_handler - Callback function converts character encoding in output buffer
* - mb_scrub - Replaces ill-formed byte sequences with substitute characters
* - mb_strlen - Get string length
* - mb_strpos - Find position of first occurrence of string in a string
* - mb_strrpos - Find position of last occurrence of a string in a string
* - mb_str_split - Convert a string to an array
* - mb_strtolower - Make a string lowercase
* - mb_strtoupper - Make a string uppercase
* - mb_substitute_character - Set/Get substitution character
* - mb_substr - Get part of string
* - mb_stripos - Finds position of first occurrence of a string within another, case insensitive
* - mb_stristr - Finds first occurrence of a string within another, case insensitive
* - mb_strrchr - Finds the last occurrence of a character in a string within another
* - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive
* - mb_strripos - Finds position of last occurrence of a string within another, case insensitive
* - mb_strstr - Finds first occurrence of a string within another
* - mb_strwidth - Return width of string
* - mb_substr_count - Count the number of substring occurrences
*
* Not implemented:
* - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
* - mb_ereg_* - Regular expression with multibyte support
* - mb_parse_str - Parse GET/POST/COOKIE data and set global variable
* - mb_preferred_mime_name - Get MIME charset string
* - mb_regex_encoding - Returns current encoding for multibyte regex as string
* - mb_regex_set_options - Set/Get the default options for mbregex functions
* - mb_send_mail - Send encoded mail
* - mb_split - Split multibyte string using regular expression
* - mb_strcut - Get part of string
* - mb_strimwidth - Get truncated string with specified width
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
final class Mbstring
{
const MB_CASE_FOLD = PHP_INT_MAX;
private static $encodingList = array('ASCII', 'UTF-8');
private static $language = 'neutral';
private static $internalEncoding = 'UTF-8';
private static $caseFold = array(
array('µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"),
array('μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'),
);
public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
{
if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
$fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
} else {
$fromEncoding = self::getEncoding($fromEncoding);
}
$toEncoding = self::getEncoding($toEncoding);
if ('BASE64' === $fromEncoding) {
$s = base64_decode($s);
$fromEncoding = $toEncoding;
}
if ('BASE64' === $toEncoding) {
return base64_encode($s);
}
if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
$fromEncoding = 'Windows-1252';
}
if ('UTF-8' !== $fromEncoding) {
$s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
}
return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s);
}
if ('HTML-ENTITIES' === $fromEncoding) {
$s = html_entity_decode($s, ENT_COMPAT, 'UTF-8');
$fromEncoding = 'UTF-8';
}
return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
}
public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null)
{
$vars = array(&$a, &$b, &$c, &$d, &$e, &$f);
$ok = true;
array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
$ok = false;
}
});
return $ok ? $fromEncoding : false;
}
public static function mb_decode_mimeheader($s)
{
return iconv_mime_decode($s, 2, self::$internalEncoding);
}
public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
{
trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING);
}
public static function mb_decode_numericentity($s, $convmap, $encoding = null)
{
if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING);
return null;
}
if (!\is_array($convmap) || !$convmap) {
return false;
}
if (null !== $encoding && !\is_scalar($encoding)) {
trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING);
return ''; // Instead of null (cf. mb_encode_numericentity).
}
$s = (string) $s;
if ('' === $s) {
return '';
}
$encoding = self::getEncoding($encoding);
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}
$cnt = floor(\count($convmap) / 4) * 4;
for ($i = 0; $i < $cnt; $i += 4) {
// collector_decode_htmlnumericentity ignores $convmap[$i + 3]
$convmap[$i] += $convmap[$i + 2];
$convmap[$i + 1] += $convmap[$i + 2];
}
$s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
$c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
for ($i = 0; $i < $cnt; $i += 4) {
if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
return Mbstring::mb_chr($c - $convmap[$i + 2]);
}
}
return $m[0];
}, $s);
if (null === $encoding) {
return $s;
}
return iconv('UTF-8', $encoding.'//IGNORE', $s);
}
public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
{
if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING);
return null;
}
if (!\is_array($convmap) || !$convmap) {
return false;
}
if (null !== $encoding && !\is_scalar($encoding)) {
trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING);
return null; // Instead of '' (cf. mb_decode_numericentity).
}
if (null !== $is_hex && !\is_scalar($is_hex)) {
trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', E_USER_WARNING);
return null;
}
$s = (string) $s;
if ('' === $s) {
return '';
}
$encoding = self::getEncoding($encoding);
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}
static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
$cnt = floor(\count($convmap) / 4) * 4;
$i = 0;
$len = \strlen($s);
$result = '';
while ($i < $len) {
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
$i += $ulen;
$c = self::mb_ord($uchr);
for ($j = 0; $j < $cnt; $j += 4) {
if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
$cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
$result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
continue 2;
}
}
$result .= $uchr;
}
if (null === $encoding) {
return $result;
}
return iconv('UTF-8', $encoding.'//IGNORE', $result);
}
public static function mb_convert_case($s, $mode, $encoding = null)
{
$s = (string) $s;
if ('' === $s) {
return '';
}
$encoding = self::getEncoding($encoding);
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}
if (MB_CASE_TITLE == $mode) {
static $titleRegexp = null;
if (null === $titleRegexp) {
$titleRegexp = self::getData('titleCaseRegexp');
}
$s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $s);
} else {
if (MB_CASE_UPPER == $mode) {
static $upper = null;
if (null === $upper) {
$upper = self::getData('upperCase');
}
$map = $upper;
} else {
if (self::MB_CASE_FOLD === $mode) {
$s = str_replace(self::$caseFold[0], self::$caseFold[1], $s);
}
static $lower = null;
if (null === $lower) {
$lower = self::getData('lowerCase');
}
$map = $lower;
}
static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
$i = 0;
$len = \strlen($s);
while ($i < $len) {
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
$uchr = substr($s, $i, $ulen);
$i += $ulen;
if (isset($map[$uchr])) {
$uchr = $map[$uchr];
$nlen = \strlen($uchr);
if ($nlen == $ulen) {
$nlen = $i;
do {
$s[--$nlen] = $uchr[--$ulen];
} while ($ulen);
} else {
$s = substr_replace($s, $uchr, $i - $ulen, $ulen);
$len += $nlen - $ulen;
$i += $nlen - $ulen;
}
}
}
}
if (null === $encoding) {
return $s;
}
return iconv('UTF-8', $encoding.'//IGNORE', $s);
}
public static function mb_internal_encoding($encoding = null)
{
if (null === $encoding) {
return self::$internalEncoding;
}
$encoding = self::getEncoding($encoding);
if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) {
self::$internalEncoding = $encoding;
return true;
}
return false;
}
public static function mb_language($lang = null)
{
if (null === $lang) {
return self::$language;
}
switch ($lang = strtolower($lang)) {
case 'uni':
case 'neutral':
self::$language = $lang;
return true;
}
return false;
}
public static function mb_list_encodings()
{
return array('UTF-8');
}
public static function mb_encoding_aliases($encoding)
{
switch (strtoupper($encoding)) {
case 'UTF8':
case 'UTF-8':
return array('utf8');
}
return false;
}
public static function mb_check_encoding($var = null, $encoding = null)
{
if (null === $encoding) {
if (null === $var) {
return false;
}
$encoding = self::$internalEncoding;
}
return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var);
}
public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
{
if (null === $encodingList) {
$encodingList = self::$encodingList;
} else {
if (!\is_array($encodingList)) {
$encodingList = array_map('trim', explode(',', $encodingList));
}
$encodingList = array_map('strtoupper', $encodingList);
}
foreach ($encodingList as $enc) {
switch ($enc) {
case 'ASCII':
if (!preg_match('/[\x80-\xFF]/', $str)) {
return $enc;
}
break;
case 'UTF8':
case 'UTF-8':
if (preg_match('//u', $str)) {
return 'UTF-8';
}
break;
default:
if (0 === strncmp($enc, 'ISO-8859-', 9)) {
return $enc;
}
}
}
return false;
}
public static function mb_detect_order($encodingList = null)
{
if (null === $encodingList) {
return self::$encodingList;
}
if (!\is_array($encodingList)) {
$encodingList = array_map('trim', explode(',', $encodingList));
}
$encodingList = array_map('strtoupper', $encodingList);
foreach ($encodingList as $enc) {
switch ($enc) {
default:
if (strncmp($enc, 'ISO-8859-', 9)) {
return false;
}
// no break
case 'ASCII':
case 'UTF8':
case 'UTF-8':
}
}
self::$encodingList = $encodingList;
return true;
}
public static function mb_strlen($s, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return \strlen($s);
}
return @iconv_strlen($s, $encoding);
}
public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return strpos($haystack, $needle, $offset);
}
$needle = (string) $needle;
if ('' === $needle) {
trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING);
return false;
}
return iconv_strpos($haystack, $needle, $offset, $encoding);
}
public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return strrpos($haystack, $needle, $offset);
}
if ($offset != (int) $offset) {
$offset = 0;
} elseif ($offset = (int) $offset) {
if ($offset < 0) {
$haystack = self::mb_substr($haystack, 0, $offset, $encoding);
$offset = 0;
} else {
$haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
}
}
$pos = iconv_strrpos($haystack, $needle, $encoding);
return false !== $pos ? $offset + $pos : false;
}
public static function mb_str_split($string, $split_length = 1, $encoding = null)
{
if (null !== $string && !\is_scalar($string) && !(\is_object($string) && \method_exists($string, '__toString'))) {
trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', E_USER_WARNING);
return null;
}
if ($split_length < 1) {
trigger_error('The length of each segment must be greater than zero', E_USER_WARNING);
return false;
}
if (null === $encoding) {
$encoding = mb_internal_encoding();
}
$result = array();
$length = mb_strlen($string, $encoding);
for ($i = 0; $i < $length; $i += $split_length) {
$result[] = mb_substr($string, $i, $split_length, $encoding);
}
return $result;
}
public static function mb_strtolower($s, $encoding = null)
{
return self::mb_convert_case($s, MB_CASE_LOWER, $encoding);
}
public static function mb_strtoupper($s, $encoding = null)
{
return self::mb_convert_case($s, MB_CASE_UPPER, $encoding);
}
public static function mb_substitute_character($c = null)
{
if (0 === strcasecmp($c, 'none')) {
return true;
}
return null !== $c ? false : 'none';
}
public static function mb_substr($s, $start, $length = null, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return (string) substr($s, $start, null === $length ? 2147483647 : $length);
}
if ($start < 0) {
$start = iconv_strlen($s, $encoding) + $start;
if ($start < 0) {
$start = 0;
}
}
if (null === $length) {
$length = 2147483647;
} elseif ($length < 0) {
$length = iconv_strlen($s, $encoding) + $length - $start;
if ($length < 0) {
return '';
}
}
return (string) iconv_substr($s, $start, $length, $encoding);
}
public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
{
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
return self::mb_strpos($haystack, $needle, $offset, $encoding);
}
public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
{
$pos = self::mb_stripos($haystack, $needle, 0, $encoding);
return self::getSubpart($pos, $part, $haystack, $encoding);
}
public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return strrchr($haystack, $needle, $part);
}
$needle = self::mb_substr($needle, 0, 1, $encoding);
$pos = iconv_strrpos($haystack, $needle, $encoding);
return self::getSubpart($pos, $part, $haystack, $encoding);
}
public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
{
$needle = self::mb_substr($needle, 0, 1, $encoding);
$pos = self::mb_strripos($haystack, $needle, $encoding);
return self::getSubpart($pos, $part, $haystack, $encoding);
}
public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
{
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
return self::mb_strrpos($haystack, $needle, $offset, $encoding);
}
public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
{
$pos = strpos($haystack, $needle);
if (false === $pos) {
return false;
}
if ($part) {
return substr($haystack, 0, $pos);
}
return substr($haystack, $pos);
}
public static function mb_get_info($type = 'all')
{
$info = array(
'internal_encoding' => self::$internalEncoding,
'http_output' => 'pass',
'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
'func_overload' => 0,
'func_overload_list' => 'no overload',
'mail_charset' => 'UTF-8',
'mail_header_encoding' => 'BASE64',
'mail_body_encoding' => 'BASE64',
'illegal_chars' => 0,
'encoding_translation' => 'Off',
'language' => self::$language,
'detect_order' => self::$encodingList,
'substitute_character' => 'none',
'strict_detection' => 'Off',
);
if ('all' === $type) {
return $info;
}
if (isset($info[$type])) {
return $info[$type];
}
return false;
}
public static function mb_http_input($type = '')
{
return false;
}
public static function mb_http_output($encoding = null)
{
return null !== $encoding ? 'pass' === $encoding : 'pass';
}
public static function mb_strwidth($s, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('UTF-8' !== $encoding) {
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}
$s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
return ($wide << 1) + iconv_strlen($s, 'UTF-8');
}
public static function mb_substr_count($haystack, $needle, $encoding = null)
{
return substr_count($haystack, $needle);
}
public static function mb_output_handler($contents, $status)
{
return $contents;
}
public static function mb_chr($code, $encoding = null)
{
if (0x80 > $code %= 0x200000) {
$s = \chr($code);
} elseif (0x800 > $code) {
$s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
} elseif (0x10000 > $code) {
$s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
} else {
$s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
}
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
$s = mb_convert_encoding($s, $encoding, 'UTF-8');
}
return $s;
}
public static function mb_ord($s, $encoding = null)
{
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
$s = mb_convert_encoding($s, 'UTF-8', $encoding);
}
if (1 === \strlen($s)) {
return \ord($s);
}
$code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
if (0xF0 <= $code) {
return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
}
if (0xE0 <= $code) {
return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
}
if (0xC0 <= $code) {
return (($code - 0xC0) << 6) + $s[2] - 0x80;
}
return $code;
}
private static function getSubpart($pos, $part, $haystack, $encoding)
{
if (false === $pos) {
return false;
}
if ($part) {
return self::mb_substr($haystack, 0, $pos, $encoding);
}
return self::mb_substr($haystack, $pos, null, $encoding);
}
private static function html_encoding_callback(array $m)
{
$i = 1;
$entities = '';
$m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8'));
while (isset($m[$i])) {
if (0x80 > $m[$i]) {
$entities .= \chr($m[$i++]);
continue;
}
if (0xF0 <= $m[$i]) {
$c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} elseif (0xE0 <= $m[$i]) {
$c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} else {
$c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
}
$entities .= '&#'.$c.';';
}
return $entities;
}
private static function title_case(array $s)
{
return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, 'UTF-8');
}
private static function getData($file)
{
if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
return require $file;
}
return false;
}
private static function getEncoding($encoding)
{
if (null === $encoding) {
return self::$internalEncoding;
}
$encoding = strtoupper($encoding);
if ('8BIT' === $encoding || 'BINARY' === $encoding) {
return 'CP850';
}
if ('UTF8' === $encoding) {
return 'UTF-8';
}
return $encoding;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Service;
/**
* A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method.
*
* The getSubscribedServices method returns an array of service types required by such instances,
* optionally keyed by the service names used internally. Service types that start with an interrogation
* mark "?" are optional, while the other ones are mandatory service dependencies.
*
* The injected service locators SHOULD NOT allow access to any other services not specified by the method.
*
* It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally.
* This interface does not dictate any injection method for these service locators, although constructor
* injection is recommended.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface ServiceSubscriberInterface
{
/**
* Returns an array of service types required by such instances, optionally keyed by the service names used internally.
*
* For mandatory dependencies:
*
* * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name
* internally to fetch a service which must implement Psr\Log\LoggerInterface.
* * ['loggers' => 'Psr\Log\LoggerInterface[]'] means the objects use the "loggers" name
* internally to fetch an iterable of Psr\Log\LoggerInterface instances.
* * ['Psr\Log\LoggerInterface'] is a shortcut for
* * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface']
*
* otherwise:
*
* * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency
* * ['loggers' => '?Psr\Log\LoggerInterface[]'] denotes an optional iterable dependency
* * ['?Psr\Log\LoggerInterface'] is a shortcut for
* * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface']
*
* @return array The required service types, optionally keyed by service names
*/
public static function getSubscribedServices();
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Service;
/**
* Provides a way to reset an object to its initial state.
*
* When calling the "reset()" method on an object, it should be put back to its
* initial state. This usually means clearing any internal buffers and forwarding
* the call to internal dependencies. All properties of the object should be put
* back to the same state it had when it was first ready to use.
*
* This method could be called, for example, to recycle objects that are used as
* services, so that they can be used to handle several requests in the same
* process loop (note that we advise making your services stateless instead of
* implementing this interface when possible.)
*/
interface ResetInterface
{
public function reset();
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Service;
use Psr\Container\ContainerInterface;
/**
* Implementation of ServiceSubscriberInterface that determines subscribed services from
* private method return types. Service ids are available as "ClassName::methodName".
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
trait ServiceSubscriberTrait
{
/** @var ContainerInterface */
protected $container;
public static function getSubscribedServices(): array
{
static $services;
if (null !== $services) {
return $services;
}
$services = \is_callable(['parent', __FUNCTION__]) ? parent::getSubscribedServices() : [];
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
continue;
}
if (self::class === $method->getDeclaringClass()->name && ($returnType = $method->getReturnType()) && !$returnType->isBuiltin()) {
$services[self::class.'::'.$method->name] = '?'.$returnType->getName();
}
}
return $services;
}
/**
* @required
*/
public function setContainer(ContainerInterface $container)
{
$this->container = $container;
if (\is_callable(['parent', __FUNCTION__])) {
return parent::setContainer($container);
}
return null;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Service;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* A trait to help implement ServiceProviderInterface.
*
* @author Robin Chalas <robin.chalas@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
trait ServiceLocatorTrait
{
private $factories;
private $loading = [];
private $providedTypes;
/**
* @param callable[] $factories
*/
public function __construct(array $factories)
{
$this->factories = $factories;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function has($id)
{
return isset($this->factories[$id]);
}
/**
* {@inheritdoc}
*/
public function get($id)
{
if (!isset($this->factories[$id])) {
throw $this->createNotFoundException($id);
}
if (isset($this->loading[$id])) {
$ids = array_values($this->loading);
$ids = \array_slice($this->loading, array_search($id, $ids));
$ids[] = $id;
throw $this->createCircularReferenceException($id, $ids);
}
$this->loading[$id] = $id;
try {
return $this->factories[$id]($this);
} finally {
unset($this->loading[$id]);
}
}
/**
* {@inheritdoc}
*/
public function getProvidedServices(): array
{
if (null === $this->providedTypes) {
$this->providedTypes = [];
foreach ($this->factories as $name => $factory) {
if (!\is_callable($factory)) {
$this->providedTypes[$name] = '?';
} else {
$type = (new \ReflectionFunction($factory))->getReturnType();
$this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').$type->getName() : '?';
}
}
}
return $this->providedTypes;
}
private function createNotFoundException(string $id): NotFoundExceptionInterface
{
if (!$alternatives = array_keys($this->factories)) {
$message = 'is empty...';
} else {
$last = array_pop($alternatives);
if ($alternatives) {
$message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last);
} else {
$message = sprintf('only knows about the "%s" service.', $last);
}
}
if ($this->loading) {
$message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message);
} else {
$message = sprintf('Service "%s" not found: the current service locator %s', $id, $message);
}
return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface {
};
}
private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface
{
return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface {
};
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Service\Test;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\ServiceLocatorTrait;
abstract class ServiceLocatorTest extends TestCase
{
protected function getServiceLocator(array $factories)
{
return new class($factories) implements ContainerInterface {
use ServiceLocatorTrait;
};
}
public function testHas()
{
$locator = $this->getServiceLocator([
'foo' => function () { return 'bar'; },
'bar' => function () { return 'baz'; },
function () { return 'dummy'; },
]);
$this->assertTrue($locator->has('foo'));
$this->assertTrue($locator->has('bar'));
$this->assertFalse($locator->has('dummy'));
}
public function testGet()
{
$locator = $this->getServiceLocator([
'foo' => function () { return 'bar'; },
'bar' => function () { return 'baz'; },
]);
$this->assertSame('bar', $locator->get('foo'));
$this->assertSame('baz', $locator->get('bar'));
}
public function testGetDoesNotMemoize()
{
$i = 0;
$locator = $this->getServiceLocator([
'foo' => function () use (&$i) {
++$i;
return 'bar';
},
]);
$this->assertSame('bar', $locator->get('foo'));
$this->assertSame('bar', $locator->get('foo'));
$this->assertSame(2, $i);
}
public function testThrowsOnUndefinedInternalService()
{
if (!$this->getExpectedException()) {
$this->expectException('Psr\Container\NotFoundExceptionInterface');
$this->expectExceptionMessage('The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.');
}
$locator = $this->getServiceLocator([
'foo' => function () use (&$locator) { return $locator->get('bar'); },
]);
$locator->get('foo');
}
public function testThrowsOnCircularReference()
{
$this->expectException('Psr\Container\ContainerExceptionInterface');
$this->expectExceptionMessage('Circular reference detected for service "bar", path: "bar -> baz -> bar".');
$locator = $this->getServiceLocator([
'foo' => function () use (&$locator) { return $locator->get('bar'); },
'bar' => function () use (&$locator) { return $locator->get('baz'); },
'baz' => function () use (&$locator) { return $locator->get('bar'); },
]);
$locator->get('foo');
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Service;
use Psr\Container\ContainerInterface;
/**
* A ServiceProviderInterface exposes the identifiers and the types of services provided by a container.
*
* @author Nicolas Grekas <p@tchwork.com>
* @author Mateusz Sip <mateusz.sip@gmail.com>
*/
interface ServiceProviderInterface extends ContainerInterface
{
/**
* Returns an associative array of service types keyed by the identifiers provided by the current container.
*
* Examples:
*
* * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface
* * ['foo' => '?'] means the container provides service name "foo" of unspecified type
* * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null
*
* @return string[] The provided service types, keyed by service names
*/
public function getProvidedServices(): array;
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Contracts\Service\ResetInterface;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class OutputFormatterStyleStack implements ResetInterface
{
/**
* @var OutputFormatterStyleInterface[]
*/
private $styles;
private $emptyStyle;
public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
{
$this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle();
$this->reset();
}
/**
* Resets stack (ie. empty internal arrays).
*/
public function reset()
{
$this->styles = [];
}
/**
* Pushes a style in the stack.
*/
public function push(OutputFormatterStyleInterface $style)
{
$this->styles[] = $style;
}
/**
* Pops a style from the stack.
*
* @return OutputFormatterStyleInterface
*
* @throws InvalidArgumentException When style tags incorrectly nested
*/
public function pop(OutputFormatterStyleInterface $style = null)
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
if (null === $style) {
return array_pop($this->styles);
}
foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
if ($style->apply('') === $stackedStyle->apply('')) {
$this->styles = \array_slice($this->styles, 0, $index);
return $stackedStyle;
}
}
throw new InvalidArgumentException('Incorrectly nested style tag found.');
}
/**
* Computes current style with stacks top codes.
*
* @return OutputFormatterStyle
*/
public function getCurrent()
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
return $this->styles[\count($this->styles) - 1];
}
/**
* @return $this
*/
public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle)
{
$this->emptyStyle = $emptyStyle;
return $this;
}
/**
* @return OutputFormatterStyleInterface
*/
public function getEmptyStyle()
{
return $this->emptyStyle;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Formatter;
/**
* Formatter style interface for defining styles.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
interface OutputFormatterStyleInterface
{
/**
* Sets style foreground color.
*
* @param string|null $color The color name
*/
public function setForeground($color = null);
/**
* Sets style background color.
*
* @param string $color The color name
*/
public function setBackground($color = null);
/**
* Sets some specific style option.
*
* @param string $option The option name
*/
public function setOption($option);
/**
* Unsets some specific style option.
*
* @param string $option The option name
*/
public function unsetOption($option);
/**
* Sets multiple style options at once.
*/
public function setOptions(array $options);
/**
* Applies the style to a given text.
*
* @param string $text The text to style
*
* @return string
*/
public function apply($text);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Formatter;
/**
* Formatter interface for console output that supports word wrapping.
*
* @author Roland Franssen <franssen.roland@gmail.com>
*/
interface WrappableOutputFormatterInterface extends OutputFormatterInterface
{
/**
* Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping).
*/
public function formatAndWrap(string $message, int $width);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* Formatter class for console output.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class OutputFormatter implements WrappableOutputFormatterInterface
{
private $decorated;
private $styles = [];
private $styleStack;
/**
* Escapes "<" special char in given text.
*
* @param string $text Text to escape
*
* @return string Escaped text
*/
public static function escape($text)
{
$text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);
return self::escapeTrailingBackslash($text);
}
/**
* Escapes trailing "\" in given text.
*
* @param string $text Text to escape
*
* @return string Escaped text
*
* @internal
*/
public static function escapeTrailingBackslash($text)
{
if ('\\' === substr($text, -1)) {
$len = \strlen($text);
$text = rtrim($text, '\\');
$text = str_replace("\0", '', $text);
$text .= str_repeat("\0", $len - \strlen($text));
}
return $text;
}
/**
* Initializes console output formatter.
*
* @param bool $decorated Whether this formatter should actually decorate strings
* @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances
*/
public function __construct(bool $decorated = false, array $styles = [])
{
$this->decorated = $decorated;
$this->setStyle('error', new OutputFormatterStyle('white', 'red'));
$this->setStyle('info', new OutputFormatterStyle('green'));
$this->setStyle('comment', new OutputFormatterStyle('yellow'));
$this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
foreach ($styles as $name => $style) {
$this->setStyle($name, $style);
}
$this->styleStack = new OutputFormatterStyleStack();
}
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
$this->decorated = (bool) $decorated;
}
/**
* {@inheritdoc}
*/
public function isDecorated()
{
return $this->decorated;
}
/**
* {@inheritdoc}
*/
public function setStyle($name, OutputFormatterStyleInterface $style)
{
$this->styles[strtolower($name)] = $style;
}
/**
* {@inheritdoc}
*/
public function hasStyle($name)
{
return isset($this->styles[strtolower($name)]);
}
/**
* {@inheritdoc}
*/
public function getStyle($name)
{
if (!$this->hasStyle($name)) {
throw new InvalidArgumentException(sprintf('Undefined style: %s', $name));
}
return $this->styles[strtolower($name)];
}
/**
* {@inheritdoc}
*/
public function format($message)
{
return $this->formatAndWrap((string) $message, 0);
}
/**
* {@inheritdoc}
*/
public function formatAndWrap(string $message, int $width)
{
$offset = 0;
$output = '';
$tagRegex = '[a-z][^<>]*+';
$currentLineLength = 0;
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
$text = $match[0];
if (0 != $pos && '\\' == $message[$pos - 1]) {
continue;
}
// add the text up to the next tag
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
$offset = $pos + \strlen($text);
// opening tag?
if ($open = '/' != $text[1]) {
$tag = $matches[1][$i][0];
} else {
$tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
}
if (!$open && !$tag) {
// </>
$this->styleStack->pop();
} elseif (false === $style = $this->createStyleFromString($tag)) {
$output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength);
} elseif ($open) {
$this->styleStack->push($style);
} else {
$this->styleStack->pop($style);
}
}
$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
if (false !== strpos($output, "\0")) {
return strtr($output, ["\0" => '\\', '\\<' => '<']);
}
return str_replace('\\<', '<', $output);
}
/**
* @return OutputFormatterStyleStack
*/
public function getStyleStack()
{
return $this->styleStack;
}
/**
* Tries to create new style instance from string.
*
* @return OutputFormatterStyle|false False if string is not format string
*/
private function createStyleFromString(string $string)
{
if (isset($this->styles[$string])) {
return $this->styles[$string];
}
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, PREG_SET_ORDER)) {
return false;
}
$style = new OutputFormatterStyle();
foreach ($matches as $match) {
array_shift($match);
$match[0] = strtolower($match[0]);
if ('fg' == $match[0]) {
$style->setForeground(strtolower($match[1]));
} elseif ('bg' == $match[0]) {
$style->setBackground(strtolower($match[1]));
} elseif ('href' === $match[0]) {
$style->setHref($match[1]);
} elseif ('options' === $match[0]) {
preg_match_all('([^,;]+)', strtolower($match[1]), $options);
$options = array_shift($options);
foreach ($options as $option) {
$style->setOption($option);
}
} else {
return false;
}
}
return $style;
}
/**
* Applies current style from stack to text, if must be applied.
*/
private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string
{
if ('' === $text) {
return '';
}
if (!$width) {
return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text;
}
if (!$currentLineLength && '' !== $current) {
$text = ltrim($text);
}
if ($currentLineLength) {
$prefix = substr($text, 0, $i = $width - $currentLineLength)."\n";
$text = substr($text, $i);
} else {
$prefix = '';
}
preg_match('~(\\n)$~', $text, $matches);
$text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text);
$text = rtrim($text, "\n").($matches[1] ?? '');
if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) {
$text = "\n".$text;
}
$lines = explode("\n", $text);
foreach ($lines as $line) {
$currentLineLength += \strlen($line);
if ($width <= $currentLineLength) {
$currentLineLength = 0;
}
}
if ($this->isDecorated()) {
foreach ($lines as $i => $line) {
$lines[$i] = $this->styleStack->getCurrent()->apply($line);
}
}
return implode("\n", $lines);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Formatter;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* Formatter style class for defining styles.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class OutputFormatterStyle implements OutputFormatterStyleInterface
{
private static $availableForegroundColors = [
'black' => ['set' => 30, 'unset' => 39],
'red' => ['set' => 31, 'unset' => 39],
'green' => ['set' => 32, 'unset' => 39],
'yellow' => ['set' => 33, 'unset' => 39],
'blue' => ['set' => 34, 'unset' => 39],
'magenta' => ['set' => 35, 'unset' => 39],
'cyan' => ['set' => 36, 'unset' => 39],
'white' => ['set' => 37, 'unset' => 39],
'default' => ['set' => 39, 'unset' => 39],
];
private static $availableBackgroundColors = [
'black' => ['set' => 40, 'unset' => 49],
'red' => ['set' => 41, 'unset' => 49],
'green' => ['set' => 42, 'unset' => 49],
'yellow' => ['set' => 43, 'unset' => 49],
'blue' => ['set' => 44, 'unset' => 49],
'magenta' => ['set' => 45, 'unset' => 49],
'cyan' => ['set' => 46, 'unset' => 49],
'white' => ['set' => 47, 'unset' => 49],
'default' => ['set' => 49, 'unset' => 49],
];
private static $availableOptions = [
'bold' => ['set' => 1, 'unset' => 22],
'underscore' => ['set' => 4, 'unset' => 24],
'blink' => ['set' => 5, 'unset' => 25],
'reverse' => ['set' => 7, 'unset' => 27],
'conceal' => ['set' => 8, 'unset' => 28],
];
private $foreground;
private $background;
private $href;
private $options = [];
private $handlesHrefGracefully;
/**
* Initializes output formatter style.
*
* @param string|null $foreground The style foreground color name
* @param string|null $background The style background color name
* @param array $options The style options
*/
public function __construct(string $foreground = null, string $background = null, array $options = [])
{
if (null !== $foreground) {
$this->setForeground($foreground);
}
if (null !== $background) {
$this->setBackground($background);
}
if (\count($options)) {
$this->setOptions($options);
}
}
/**
* {@inheritdoc}
*/
public function setForeground($color = null)
{
if (null === $color) {
$this->foreground = null;
return;
}
if (!isset(static::$availableForegroundColors[$color])) {
throw new InvalidArgumentException(sprintf('Invalid foreground color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableForegroundColors))));
}
$this->foreground = static::$availableForegroundColors[$color];
}
/**
* {@inheritdoc}
*/
public function setBackground($color = null)
{
if (null === $color) {
$this->background = null;
return;
}
if (!isset(static::$availableBackgroundColors[$color])) {
throw new InvalidArgumentException(sprintf('Invalid background color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableBackgroundColors))));
}
$this->background = static::$availableBackgroundColors[$color];
}
public function setHref(string $url): void
{
$this->href = $url;
}
/**
* {@inheritdoc}
*/
public function setOption($option)
{
if (!isset(static::$availableOptions[$option])) {
throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
}
if (!\in_array(static::$availableOptions[$option], $this->options)) {
$this->options[] = static::$availableOptions[$option];
}
}
/**
* {@inheritdoc}
*/
public function unsetOption($option)
{
if (!isset(static::$availableOptions[$option])) {
throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
}
$pos = array_search(static::$availableOptions[$option], $this->options);
if (false !== $pos) {
unset($this->options[$pos]);
}
}
/**
* {@inheritdoc}
*/
public function setOptions(array $options)
{
$this->options = [];
foreach ($options as $option) {
$this->setOption($option);
}
}
/**
* {@inheritdoc}
*/
public function apply($text)
{
$setCodes = [];
$unsetCodes = [];
if (null === $this->handlesHrefGracefully) {
$this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION');
}
if (null !== $this->foreground) {
$setCodes[] = $this->foreground['set'];
$unsetCodes[] = $this->foreground['unset'];
}
if (null !== $this->background) {
$setCodes[] = $this->background['set'];
$unsetCodes[] = $this->background['unset'];
}
foreach ($this->options as $option) {
$setCodes[] = $option['set'];
$unsetCodes[] = $option['unset'];
}
if (null !== $this->href && $this->handlesHrefGracefully) {
$text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";
}
if (0 === \count($setCodes)) {
return $text;
}
return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes));
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Formatter;
/**
* Formatter interface for console output.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
interface OutputFormatterInterface
{
/**
* Sets the decorated flag.
*
* @param bool $decorated Whether to decorate the messages or not
*/
public function setDecorated($decorated);
/**
* Gets the decorated flag.
*
* @return bool true if the output will decorate messages, false otherwise
*/
public function isDecorated();
/**
* Sets a new style.
*
* @param string $name The style name
* @param OutputFormatterStyleInterface $style The style instance
*/
public function setStyle($name, OutputFormatterStyleInterface $style);
/**
* Checks if output formatter has style with specified name.
*
* @param string $name
*
* @return bool
*/
public function hasStyle($name);
/**
* Gets style options from style with specified name.
*
* @param string $name
*
* @return OutputFormatterStyleInterface
*
* @throws \InvalidArgumentException When style isn't defined
*/
public function getStyle($name);
/**
* Formats a message according to the given styles.
*
* @param string $message The message to style
*
* @return string The styled message
*/
public function format($message);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
/**
* InputInterface is the interface implemented by all input classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface InputInterface
{
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string|null The value of the first argument or null otherwise
*/
public function getFirstArgument();
/**
* Returns true if the raw parameters (not parsed) contain a value.
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
* Does not necessarily return the correct result for short options
* when multiple flags are combined in the same option.
*
* @param string|array $values The values to look for in the raw parameters (can be an array)
* @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
*
* @return bool true if the value is contained in the raw parameters
*/
public function hasParameterOption($values, $onlyParams = false);
/**
* Returns the value of a raw option (not parsed).
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
* Does not necessarily return the correct result for short options
* when multiple flags are combined in the same option.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param mixed $default The default value to return if no result is found
* @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
*
* @return mixed The option value
*/
public function getParameterOption($values, $default = false, $onlyParams = false);
/**
* Binds the current Input instance with the given arguments and options.
*
* @throws RuntimeException
*/
public function bind(InputDefinition $definition);
/**
* Validates the input.
*
* @throws RuntimeException When not enough arguments are given
*/
public function validate();
/**
* Returns all the given arguments merged with the default values.
*
* @return array
*/
public function getArguments();
/**
* Returns the argument value for a given argument name.
*
* @param string $name The argument name
*
* @return string|string[]|null The argument value
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name);
/**
* Sets an argument value by name.
*
* @param string $name The argument name
* @param string|string[]|null $value The argument value
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function setArgument($name, $value);
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|int $name The InputArgument name or position
*
* @return bool true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name);
/**
* Returns all the given options merged with the default values.
*
* @return array
*/
public function getOptions();
/**
* Returns the option value for a given option name.
*
* @param string $name The option name
*
* @return string|string[]|bool|null The option value
*
* @throws InvalidArgumentException When option given doesn't exist
*/
public function getOption($name);
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string|string[]|bool|null $value The option value
*
* @throws InvalidArgumentException When option given doesn't exist
*/
public function setOption($name, $value);
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasOption($name);
/**
* Is this input means interactive?
*
* @return bool
*/
public function isInteractive();
/**
* Sets the input interactivity.
*
* @param bool $interactive If the input should be interactive
*/
public function setInteractive($interactive);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Represents a command line argument.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class InputArgument
{
const REQUIRED = 1;
const OPTIONAL = 2;
const IS_ARRAY = 4;
private $name;
private $mode;
private $default;
private $description;
/**
* @param string $name The argument name
* @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param string $description A description text
* @param string|string[]|null $default The default value (for self::OPTIONAL mode only)
*
* @throws InvalidArgumentException When argument mode is not valid
*/
public function __construct(string $name, int $mode = null, string $description = '', $default = null)
{
if (null === $mode) {
$mode = self::OPTIONAL;
} elseif ($mode > 7 || $mode < 1) {
throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->mode = $mode;
$this->description = $description;
$this->setDefault($default);
}
/**
* Returns the argument name.
*
* @return string The argument name
*/
public function getName()
{
return $this->name;
}
/**
* Returns true if the argument is required.
*
* @return bool true if parameter mode is self::REQUIRED, false otherwise
*/
public function isRequired()
{
return self::REQUIRED === (self::REQUIRED & $this->mode);
}
/**
* Returns true if the argument can take multiple values.
*
* @return bool true if mode is self::IS_ARRAY, false otherwise
*/
public function isArray()
{
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
}
/**
* Sets the default value.
*
* @param string|string[]|null $default The default value
*
* @throws LogicException When incorrect default value is given
*/
public function setDefault($default = null)
{
if (self::REQUIRED === $this->mode && null !== $default) {
throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = [];
} elseif (!\is_array($default)) {
throw new LogicException('A default value for an array argument must be an array.');
}
}
$this->default = $default;
}
/**
* Returns the default value.
*
* @return string|string[]|null The default value
*/
public function getDefault()
{
return $this->default;
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
{
return $this->description;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
/**
* InputAwareInterface should be implemented by classes that depends on the
* Console Input.
*
* @author Wouter J <waldio.webdesign@gmail.com>
*/
interface InputAwareInterface
{
/**
* Sets the Console Input.
*/
public function setInput(InputInterface $input);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\InvalidOptionException;
/**
* ArrayInput represents an input provided as an array.
*
* Usage:
*
* $input = new ArrayInput(['command' => 'foo:bar', 'foo' => 'bar', '--bar' => 'foobar']);
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ArrayInput extends Input
{
private $parameters;
public function __construct(array $parameters, InputDefinition $definition = null)
{
$this->parameters = $parameters;
parent::__construct($definition);
}
/**
* {@inheritdoc}
*/
public function getFirstArgument()
{
foreach ($this->parameters as $key => $value) {
if ($key && '-' === $key[0]) {
continue;
}
return $value;
}
return null;
}
/**
* {@inheritdoc}
*/
public function hasParameterOption($values, $onlyParams = false)
{
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if (!\is_int($k)) {
$v = $k;
}
if ($onlyParams && '--' === $v) {
return false;
}
if (\in_array($v, $values)) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function getParameterOption($values, $default = false, $onlyParams = false)
{
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if ($onlyParams && ('--' === $k || (\is_int($k) && '--' === $v))) {
return $default;
}
if (\is_int($k)) {
if (\in_array($v, $values)) {
return true;
}
} elseif (\in_array($k, $values)) {
return $v;
}
}
return $default;
}
/**
* Returns a stringified representation of the args passed to the command.
*
* @return string
*/
public function __toString()
{
$params = [];
foreach ($this->parameters as $param => $val) {
if ($param && '-' === $param[0]) {
if (\is_array($val)) {
foreach ($val as $v) {
$params[] = $param.('' != $v ? '='.$this->escapeToken($v) : '');
}
} else {
$params[] = $param.('' != $val ? '='.$this->escapeToken($val) : '');
}
} else {
$params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val);
}
}
return implode(' ', $params);
}
/**
* {@inheritdoc}
*/
protected function parse()
{
foreach ($this->parameters as $key => $value) {
if ('--' === $key) {
return;
}
if (0 === strpos($key, '--')) {
$this->addLongOption(substr($key, 2), $value);
} elseif (0 === strpos($key, '-')) {
$this->addShortOption(substr($key, 1), $value);
} else {
$this->addArgument($key, $value);
}
}
}
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws InvalidOptionException When option given doesn't exist
*/
private function addShortOption($shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws InvalidOptionException When option given doesn't exist
* @throws InvalidOptionException When a required value is missing
*/
private function addLongOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
}
$option = $this->definition->getOption($name);
if (null === $value) {
if ($option->isValueRequired()) {
throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name));
}
if (!$option->isValueOptional()) {
$value = true;
}
}
$this->options[$name] = $value;
}
/**
* Adds an argument value.
*
* @param string $name The argument name
* @param mixed $value The value for the argument
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
private function addArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
/**
* StreamableInputInterface is the interface implemented by all input classes
* that have an input stream.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
interface StreamableInputInterface extends InputInterface
{
/**
* Sets the input stream to read from when interacting with the user.
*
* This is mainly useful for testing purpose.
*
* @param resource $stream The input stream
*/
public function setStream($stream);
/**
* Returns the input stream.
*
* @return resource|null
*/
public function getStream();
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Represents a command line option.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class InputOption
{
const VALUE_NONE = 1;
const VALUE_REQUIRED = 2;
const VALUE_OPTIONAL = 4;
const VALUE_IS_ARRAY = 8;
private $name;
private $shortcut;
private $mode;
private $default;
private $description;
/**
* @param string $name The option name
* @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @param int|null $mode The option mode: One of the VALUE_* constants
* @param string $description A description text
* @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE)
*
* @throws InvalidArgumentException If option mode is invalid or incompatible
*/
public function __construct(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
{
if (0 === strpos($name, '--')) {
$name = substr($name, 2);
}
if (empty($name)) {
throw new InvalidArgumentException('An option name cannot be empty.');
}
if (empty($shortcut)) {
$shortcut = null;
}
if (null !== $shortcut) {
if (\is_array($shortcut)) {
$shortcut = implode('|', $shortcut);
}
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
$shortcuts = array_filter($shortcuts);
$shortcut = implode('|', $shortcuts);
if (empty($shortcut)) {
throw new InvalidArgumentException('An option shortcut cannot be empty.');
}
}
if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif ($mode > 15 || $mode < 1) {
throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->description = $description;
if ($this->isArray() && !$this->acceptValue()) {
throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
}
$this->setDefault($default);
}
/**
* Returns the option shortcut.
*
* @return string|null The shortcut
*/
public function getShortcut()
{
return $this->shortcut;
}
/**
* Returns the option name.
*
* @return string The name
*/
public function getName()
{
return $this->name;
}
/**
* Returns true if the option accepts a value.
*
* @return bool true if value mode is not self::VALUE_NONE, false otherwise
*/
public function acceptValue()
{
return $this->isValueRequired() || $this->isValueOptional();
}
/**
* Returns true if the option requires a value.
*
* @return bool true if value mode is self::VALUE_REQUIRED, false otherwise
*/
public function isValueRequired()
{
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
}
/**
* Returns true if the option takes an optional value.
*
* @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise
*/
public function isValueOptional()
{
return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
}
/**
* Returns true if the option can take multiple values.
*
* @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise
*/
public function isArray()
{
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}
/**
* Sets the default value.
*
* @param string|string[]|int|bool|null $default The default value
*
* @throws LogicException When incorrect default value is given
*/
public function setDefault($default = null)
{
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = [];
} elseif (!\is_array($default)) {
throw new LogicException('A default value for an array option must be an array.');
}
}
$this->default = $this->acceptValue() ? $default : false;
}
/**
* Returns the default value.
*
* @return string|string[]|int|bool|null The default value
*/
public function getDefault()
{
return $this->default;
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
{
return $this->description;
}
/**
* Checks whether the given option equals this one.
*
* @return bool
*/
public function equals(self $option)
{
return $option->getName() === $this->getName()
&& $option->getShortcut() === $this->getShortcut()
&& $option->getDefault() === $this->getDefault()
&& $option->isArray() === $this->isArray()
&& $option->isValueRequired() === $this->isValueRequired()
&& $option->isValueOptional() === $this->isValueOptional()
;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* StringInput represents an input provided as a string.
*
* Usage:
*
* $input = new StringInput('foo --bar="foobar"');
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class StringInput extends ArgvInput
{
const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
/**
* @param string $input A string representing the parameters from the CLI
*/
public function __construct(string $input)
{
parent::__construct([]);
$this->setTokens($this->tokenize($input));
}
/**
* Tokenizes a string.
*
* @param string $input The input to tokenize
*
* @return array An array of tokens
*
* @throws InvalidArgumentException When unable to parse input (should never happen)
*/
private function tokenize($input)
{
$tokens = [];
$length = \strlen($input);
$cursor = 0;
while ($cursor < $length) {
if (preg_match('/\s+/A', $input, $match, null, $cursor)) {
} elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
$tokens[] = $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, \strlen($match[3]) - 2)));
} elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) {
$tokens[] = stripcslashes(substr($match[0], 1, \strlen($match[0]) - 2));
} elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) {
$tokens[] = stripcslashes($match[1]);
} else {
// should never happen
throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
}
$cursor += \strlen($match[0]);
}
return $tokens;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
/**
* Input is the base class for all concrete Input classes.
*
* Three concrete classes are provided by default:
*
* * `ArgvInput`: The input comes from the CLI arguments (argv)
* * `StringInput`: The input is provided as a string
* * `ArrayInput`: The input is provided as an array
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Input implements InputInterface, StreamableInputInterface
{
protected $definition;
protected $stream;
protected $options = [];
protected $arguments = [];
protected $interactive = true;
public function __construct(InputDefinition $definition = null)
{
if (null === $definition) {
$this->definition = new InputDefinition();
} else {
$this->bind($definition);
$this->validate();
}
}
/**
* {@inheritdoc}
*/
public function bind(InputDefinition $definition)
{
$this->arguments = [];
$this->options = [];
$this->definition = $definition;
$this->parse();
}
/**
* Processes command line arguments.
*/
abstract protected function parse();
/**
* {@inheritdoc}
*/
public function validate()
{
$definition = $this->definition;
$givenArguments = $this->arguments;
$missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
});
if (\count($missingArguments) > 0) {
throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
}
}
/**
* {@inheritdoc}
*/
public function isInteractive()
{
return $this->interactive;
}
/**
* {@inheritdoc}
*/
public function setInteractive($interactive)
{
$this->interactive = (bool) $interactive;
}
/**
* {@inheritdoc}
*/
public function getArguments()
{
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
}
/**
* {@inheritdoc}
*/
public function getArgument($name)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
}
/**
* {@inheritdoc}
*/
public function setArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
}
/**
* {@inheritdoc}
*/
public function hasArgument($name)
{
return $this->definition->hasArgument($name);
}
/**
* {@inheritdoc}
*/
public function getOptions()
{
return array_merge($this->definition->getOptionDefaults(), $this->options);
}
/**
* {@inheritdoc}
*/
public function getOption($name)
{
if (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}
/**
* {@inheritdoc}
*/
public function setOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
$this->options[$name] = $value;
}
/**
* {@inheritdoc}
*/
public function hasOption($name)
{
return $this->definition->hasOption($name);
}
/**
* Escapes a token through escapeshellarg if it contains unsafe chars.
*
* @param string $token
*
* @return string
*/
public function escapeToken($token)
{
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
}
/**
* {@inheritdoc}
*/
public function setStream($stream)
{
$this->stream = $stream;
}
/**
* {@inheritdoc}
*/
public function getStream()
{
return $this->stream;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* A InputDefinition represents a set of valid command line arguments and options.
*
* Usage:
*
* $definition = new InputDefinition([
* new InputArgument('name', InputArgument::REQUIRED),
* new InputOption('foo', 'f', InputOption::VALUE_REQUIRED),
* ]);
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class InputDefinition
{
private $arguments;
private $requiredCount;
private $hasAnArrayArgument = false;
private $hasOptional;
private $options;
private $shortcuts;
/**
* @param array $definition An array of InputArgument and InputOption instance
*/
public function __construct(array $definition = [])
{
$this->setDefinition($definition);
}
/**
* Sets the definition of the input.
*/
public function setDefinition(array $definition)
{
$arguments = [];
$options = [];
foreach ($definition as $item) {
if ($item instanceof InputOption) {
$options[] = $item;
} else {
$arguments[] = $item;
}
}
$this->setArguments($arguments);
$this->setOptions($options);
}
/**
* Sets the InputArgument objects.
*
* @param InputArgument[] $arguments An array of InputArgument objects
*/
public function setArguments($arguments = [])
{
$this->arguments = [];
$this->requiredCount = 0;
$this->hasOptional = false;
$this->hasAnArrayArgument = false;
$this->addArguments($arguments);
}
/**
* Adds an array of InputArgument objects.
*
* @param InputArgument[] $arguments An array of InputArgument objects
*/
public function addArguments($arguments = [])
{
if (null !== $arguments) {
foreach ($arguments as $argument) {
$this->addArgument($argument);
}
}
}
/**
* @throws LogicException When incorrect argument is given
*/
public function addArgument(InputArgument $argument)
{
if (isset($this->arguments[$argument->getName()])) {
throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
}
if ($this->hasAnArrayArgument) {
throw new LogicException('Cannot add an argument after an array argument.');
}
if ($argument->isRequired() && $this->hasOptional) {
throw new LogicException('Cannot add a required argument after an optional one.');
}
if ($argument->isArray()) {
$this->hasAnArrayArgument = true;
}
if ($argument->isRequired()) {
++$this->requiredCount;
} else {
$this->hasOptional = true;
}
$this->arguments[$argument->getName()] = $argument;
}
/**
* Returns an InputArgument by name or by position.
*
* @param string|int $name The InputArgument name or position
*
* @return InputArgument An InputArgument object
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name)
{
if (!$this->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;
return $arguments[$name];
}
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|int $name The InputArgument name or position
*
* @return bool true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name)
{
$arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;
return isset($arguments[$name]);
}
/**
* Gets the array of InputArgument objects.
*
* @return InputArgument[] An array of InputArgument objects
*/
public function getArguments()
{
return $this->arguments;
}
/**
* Returns the number of InputArguments.
*
* @return int The number of InputArguments
*/
public function getArgumentCount()
{
return $this->hasAnArrayArgument ? PHP_INT_MAX : \count($this->arguments);
}
/**
* Returns the number of required InputArguments.
*
* @return int The number of required InputArguments
*/
public function getArgumentRequiredCount()
{
return $this->requiredCount;
}
/**
* Gets the default values.
*
* @return array An array of default values
*/
public function getArgumentDefaults()
{
$values = [];
foreach ($this->arguments as $argument) {
$values[$argument->getName()] = $argument->getDefault();
}
return $values;
}
/**
* Sets the InputOption objects.
*
* @param InputOption[] $options An array of InputOption objects
*/
public function setOptions($options = [])
{
$this->options = [];
$this->shortcuts = [];
$this->addOptions($options);
}
/**
* Adds an array of InputOption objects.
*
* @param InputOption[] $options An array of InputOption objects
*/
public function addOptions($options = [])
{
foreach ($options as $option) {
$this->addOption($option);
}
}
/**
* @throws LogicException When option given already exist
*/
public function addOption(InputOption $option)
{
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
}
}
}
$this->options[$option->getName()] = $option;
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
$this->shortcuts[$shortcut] = $option->getName();
}
}
}
/**
* Returns an InputOption by name.
*
* @param string $name The InputOption name
*
* @return InputOption A InputOption object
*
* @throws InvalidArgumentException When option given doesn't exist
*/
public function getOption($name)
{
if (!$this->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
return $this->options[$name];
}
/**
* Returns true if an InputOption object exists by name.
*
* This method can't be used to check if the user included the option when
* executing the command (use getOption() instead).
*
* @param string $name The InputOption name
*
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
/**
* Gets the array of InputOption objects.
*
* @return InputOption[] An array of InputOption objects
*/
public function getOptions()
{
return $this->options;
}
/**
* Returns true if an InputOption object exists by shortcut.
*
* @param string $name The InputOption shortcut
*
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasShortcut($name)
{
return isset($this->shortcuts[$name]);
}
/**
* Gets an InputOption by shortcut.
*
* @param string $shortcut The Shortcut name
*
* @return InputOption An InputOption object
*/
public function getOptionForShortcut($shortcut)
{
return $this->getOption($this->shortcutToName($shortcut));
}
/**
* Gets an array of default values.
*
* @return array An array of all default values
*/
public function getOptionDefaults()
{
$values = [];
foreach ($this->options as $option) {
$values[$option->getName()] = $option->getDefault();
}
return $values;
}
/**
* Returns the InputOption name given a shortcut.
*
* @param string $shortcut The shortcut
*
* @return string The InputOption name
*
* @throws InvalidArgumentException When option given does not exist
*
* @internal
*/
public function shortcutToName($shortcut)
{
if (!isset($this->shortcuts[$shortcut])) {
throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
return $this->shortcuts[$shortcut];
}
/**
* Gets the synopsis.
*
* @param bool $short Whether to return the short version (with options folded) or not
*
* @return string The synopsis
*/
public function getSynopsis($short = false)
{
$elements = [];
if ($short && $this->getOptions()) {
$elements[] = '[options]';
} elseif (!$short) {
foreach ($this->getOptions() as $option) {
$value = '';
if ($option->acceptValue()) {
$value = sprintf(
' %s%s%s',
$option->isValueOptional() ? '[' : '',
strtoupper($option->getName()),
$option->isValueOptional() ? ']' : ''
);
}
$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
$elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value);
}
}
if (\count($elements) && $this->getArguments()) {
$elements[] = '[--]';
}
$tail = '';
foreach ($this->getArguments() as $argument) {
$element = '<'.$argument->getName().'>';
if ($argument->isArray()) {
$element .= '...';
}
if (!$argument->isRequired()) {
$element = '['.$element;
$tail .= ']';
}
$elements[] = $element;
}
return implode(' ', $elements).$tail;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Input;
use Symfony\Component\Console\Exception\RuntimeException;
/**
* ArgvInput represents an input coming from the CLI arguments.
*
* Usage:
*
* $input = new ArgvInput();
*
* By default, the `$_SERVER['argv']` array is used for the input values.
*
* This can be overridden by explicitly passing the input values in the constructor:
*
* $input = new ArgvInput($_SERVER['argv']);
*
* If you pass it yourself, don't forget that the first element of the array
* is the name of the running application.
*
* When passing an argument to the constructor, be sure that it respects
* the same rules as the argv one. It's almost always better to use the
* `StringInput` when you want to provide your own input.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
* @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
*/
class ArgvInput extends Input
{
private $tokens;
private $parsed;
/**
* @param array|null $argv An array of parameters from the CLI (in the argv format)
* @param InputDefinition|null $definition A InputDefinition instance
*/
public function __construct(array $argv = null, InputDefinition $definition = null)
{
if (null === $argv) {
$argv = $_SERVER['argv'];
}
// strip the application name
array_shift($argv);
$this->tokens = $argv;
parent::__construct($definition);
}
protected function setTokens(array $tokens)
{
$this->tokens = $tokens;
}
/**
* {@inheritdoc}
*/
protected function parse()
{
$parseOptions = true;
$this->parsed = $this->tokens;
while (null !== $token = array_shift($this->parsed)) {
if ($parseOptions && '' == $token) {
$this->parseArgument($token);
} elseif ($parseOptions && '--' == $token) {
$parseOptions = false;
} elseif ($parseOptions && 0 === strpos($token, '--')) {
$this->parseLongOption($token);
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
$this->parseShortOption($token);
} else {
$this->parseArgument($token);
}
}
}
/**
* Parses a short option.
*
* @param string $token The current token
*/
private function parseShortOption($token)
{
$name = substr($token, 1);
if (\strlen($name) > 1) {
if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
// an option with a value (with no space)
$this->addShortOption($name[0], substr($name, 1));
} else {
$this->parseShortOptionSet($name);
}
} else {
$this->addShortOption($name, null);
}
}
/**
* Parses a short option set.
*
* @param string $name The current token
*
* @throws RuntimeException When option given doesn't exist
*/
private function parseShortOptionSet($name)
{
$len = \strlen($name);
for ($i = 0; $i < $len; ++$i) {
if (!$this->definition->hasShortcut($name[$i])) {
$encoding = mb_detect_encoding($name, null, true);
throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding)));
}
$option = $this->definition->getOptionForShortcut($name[$i]);
if ($option->acceptValue()) {
$this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
break;
} else {
$this->addLongOption($option->getName(), null);
}
}
}
/**
* Parses a long option.
*
* @param string $token The current token
*/
private function parseLongOption($token)
{
$name = substr($token, 2);
if (false !== $pos = strpos($name, '=')) {
if (0 === \strlen($value = substr($name, $pos + 1))) {
array_unshift($this->parsed, $value);
}
$this->addLongOption(substr($name, 0, $pos), $value);
} else {
$this->addLongOption($name, null);
}
}
/**
* Parses an argument.
*
* @param string $token The current token
*
* @throws RuntimeException When too many arguments are given
*/
private function parseArgument($token)
{
$c = \count($this->arguments);
// if input is expecting another argument, add it
if ($this->definition->hasArgument($c)) {
$arg = $this->definition->getArgument($c);
$this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
// if last argument isArray(), append token to last argument
} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
$arg = $this->definition->getArgument($c - 1);
$this->arguments[$arg->getName()][] = $token;
// unexpected argument
} else {
$all = $this->definition->getArguments();
if (\count($all)) {
throw new RuntimeException(sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))));
}
throw new RuntimeException(sprintf('No arguments expected, got "%s".', $token));
}
}
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws RuntimeException When option given doesn't exist
*/
private function addShortOption($shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws RuntimeException When option given doesn't exist
*/
private function addLongOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
}
$option = $this->definition->getOption($name);
if (null !== $value && !$option->acceptValue()) {
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) {
// if option accepts an optional or mandatory argument
// let's see if there is one provided
$next = array_shift($this->parsed);
if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) {
$value = $next;
} else {
array_unshift($this->parsed, $next);
}
}
if (null === $value) {
if ($option->isValueRequired()) {
throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}
if (!$option->isArray() && !$option->isValueOptional()) {
$value = true;
}
}
if ($option->isArray()) {
$this->options[$name][] = $value;
} else {
$this->options[$name] = $value;
}
}
/**
* {@inheritdoc}
*/
public function getFirstArgument()
{
$isOption = false;
foreach ($this->tokens as $i => $token) {
if ($token && '-' === $token[0]) {
if (false !== strpos($token, '=') || !isset($this->tokens[$i + 1])) {
continue;
}
// If it's a long option, consider that everything after "--" is the option name.
// Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator)
$name = '-' === $token[1] ? substr($token, 2) : substr($token, -1);
if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) {
// noop
} elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) {
$isOption = true;
}
continue;
}
if ($isOption) {
$isOption = false;
continue;
}
return $token;
}
return null;
}
/**
* {@inheritdoc}
*/
public function hasParameterOption($values, $onlyParams = false)
{
$values = (array) $values;
foreach ($this->tokens as $token) {
if ($onlyParams && '--' === $token) {
return false;
}
foreach ($values as $value) {
// Options with values:
// For long options, test for '--option=' at beginning
// For short options, test for '-o' at beginning
$leading = 0 === strpos($value, '--') ? $value.'=' : $value;
if ($token === $value || '' !== $leading && 0 === strpos($token, $leading)) {
return true;
}
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function getParameterOption($values, $default = false, $onlyParams = false)
{
$values = (array) $values;
$tokens = $this->tokens;
while (0 < \count($tokens)) {
$token = array_shift($tokens);
if ($onlyParams && '--' === $token) {
return $default;
}
foreach ($values as $value) {
if ($token === $value) {
return array_shift($tokens);
}
// Options with values:
// For long options, test for '--option=' at beginning
// For short options, test for '-o' at beginning
$leading = 0 === strpos($value, '--') ? $value.'=' : $value;
if ('' !== $leading && 0 === strpos($token, $leading)) {
return substr($token, \strlen($leading));
}
}
}
return $default;
}
/**
* Returns a stringified representation of the args passed to the command.
*
* @return string
*/
public function __toString()
{
$tokens = array_map(function ($token) {
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
return $match[1].$this->escapeToken($match[2]);
}
if ($token && '-' !== $token[0]) {
return $this->escapeToken($token);
}
return $token;
}, $this->tokens);
return implode(' ', $tokens);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Allows to manipulate the exit code of a command after its execution.
*
* @author Francesco Levorato <git@flevour.net>
*/
class ConsoleTerminateEvent extends ConsoleEvent
{
private $exitCode;
public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $exitCode)
{
parent::__construct($command, $input, $output);
$this->setExitCode($exitCode);
}
/**
* Sets the exit code.
*
* @param int $exitCode The command exit code
*/
public function setExitCode($exitCode)
{
$this->exitCode = (int) $exitCode;
}
/**
* Gets the exit code.
*
* @return int The command exit code
*/
public function getExitCode()
{
return $this->exitCode;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Allows to handle throwables thrown while running a command.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
final class ConsoleErrorEvent extends ConsoleEvent
{
private $error;
private $exitCode;
public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, Command $command = null)
{
parent::__construct($command, $input, $output);
$this->error = $error;
}
public function getError(): \Throwable
{
return $this->error;
}
public function setError(\Throwable $error): void
{
$this->error = $error;
}
public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;
$r = new \ReflectionProperty($this->error, 'code');
$r->setAccessible(true);
$r->setValue($this->error, $this->exitCode);
}
public function getExitCode(): int
{
return null !== $this->exitCode ? $this->exitCode : (\is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Event;
/**
* Allows to do things before the command is executed, like skipping the command or changing the input.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConsoleCommandEvent extends ConsoleEvent
{
/**
* The return code for skipped commands, this will also be passed into the terminate event.
*/
const RETURN_CODE_DISABLED = 113;
/**
* Indicates if the command should be run or skipped.
*/
private $commandShouldRun = true;
/**
* Disables the command, so it won't be run.
*
* @return bool
*/
public function disableCommand()
{
return $this->commandShouldRun = false;
}
/**
* Enables the command.
*
* @return bool
*/
public function enableCommand()
{
return $this->commandShouldRun = true;
}
/**
* Returns true if the command is runnable, false otherwise.
*
* @return bool
*/
public function commandShouldRun()
{
return $this->commandShouldRun;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\Event;
/**
* Allows to inspect input and output of a command.
*
* @author Francesco Levorato <git@flevour.net>
*/
class ConsoleEvent extends Event
{
protected $command;
private $input;
private $output;
public function __construct(Command $command = null, InputInterface $input, OutputInterface $output)
{
$this->command = $command;
$this->input = $input;
$this->output = $output;
}
/**
* Gets the command that is executed.
*
* @return Command|null A Command instance
*/
public function getCommand()
{
return $this->command;
}
/**
* Gets the input instance.
*
* @return InputInterface An InputInterface instance
*/
public function getInput()
{
return $this->input;
}
/**
* Gets the output instance.
*
* @return OutputInterface An OutputInterface instance
*/
public function getOutput()
{
return $this->output;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\DependencyInjection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\TypedReference;
/**
* Registers console commands.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class AddConsoleCommandPass implements CompilerPassInterface
{
private $commandLoaderServiceId;
private $commandTag;
public function __construct(string $commandLoaderServiceId = 'console.command_loader', string $commandTag = 'console.command')
{
$this->commandLoaderServiceId = $commandLoaderServiceId;
$this->commandTag = $commandTag;
}
public function process(ContainerBuilder $container)
{
$commandServices = $container->findTaggedServiceIds($this->commandTag, true);
$lazyCommandMap = [];
$lazyCommandRefs = [];
$serviceIds = [];
foreach ($commandServices as $id => $tags) {
$definition = $container->getDefinition($id);
$class = $container->getParameterBag()->resolveValue($definition->getClass());
if (isset($tags[0]['command'])) {
$commandName = $tags[0]['command'];
} else {
if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
}
$commandName = $class::getDefaultName();
}
if (null === $commandName) {
if (!$definition->isPublic() || $definition->isPrivate()) {
$commandId = 'console.command.public_alias.'.$id;
$container->setAlias($commandId, $id)->setPublic(true);
$id = $commandId;
}
$serviceIds[] = $id;
continue;
}
unset($tags[0]);
$lazyCommandMap[$commandName] = $id;
$lazyCommandRefs[$id] = new TypedReference($id, $class);
$aliases = [];
foreach ($tags as $tag) {
if (isset($tag['command'])) {
$aliases[] = $tag['command'];
$lazyCommandMap[$tag['command']] = $id;
}
}
$definition->addMethodCall('setName', [$commandName]);
if ($aliases) {
$definition->addMethodCall('setAliases', [$aliases]);
}
}
$container
->register($this->commandLoaderServiceId, ContainerCommandLoader::class)
->setPublic(true)
->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]);
$container->setParameter('console.command.ids', $serviceIds);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Exception;
/**
* Represents an incorrect namespace typed in the console.
*
* @author Pierre du Plessis <pdples@gmail.com>
*/
class NamespaceNotFoundException extends CommandNotFoundException
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Exception;
/**
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class LogicException extends \LogicException implements ExceptionInterface
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Exception;
/**
* Represents an incorrect command name typed in the console.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface
{
private $alternatives;
/**
* @param string $message Exception message to throw
* @param array $alternatives List of similar defined names
* @param int $code Exception code
* @param \Throwable $previous Previous exception used for the exception chaining
*/
public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->alternatives = $alternatives;
}
/**
* @return array A list of similar defined names
*/
public function getAlternatives()
{
return $this->alternatives;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Exception;
/**
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Exception;
/**
* ExceptionInterface.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
interface ExceptionInterface extends \Throwable
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Exception;
/**
* Represents an incorrect option name typed in the console.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Exception;
/**
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tester;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
/**
* Eases the testing of console commands.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class CommandTester
{
use TesterTrait;
private $command;
private $input;
private $statusCode;
public function __construct(Command $command)
{
$this->command = $command;
}
/**
* Executes the command.
*
* Available execution options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
* * capture_stderr_separately: Make output of stdOut and stdErr separately available
*
* @param array $input An array of command arguments and options
* @param array $options An array of execution options
*
* @return int The command exit code
*/
public function execute(array $input, array $options = [])
{
// set the command name automatically if the application requires
// this argument and no command name was passed
if (!isset($input['command'])
&& (null !== $application = $this->command->getApplication())
&& $application->getDefinition()->hasArgument('command')
) {
$input = array_merge(['command' => $this->command->getName()], $input);
}
$this->input = new ArrayInput($input);
// Use an in-memory input stream even if no inputs are set so that QuestionHelper::ask() does not rely on the blocking STDIN.
$this->input->setStream(self::createStream($this->inputs));
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
if (!isset($options['decorated'])) {
$options['decorated'] = false;
}
$this->initOutput($options);
return $this->statusCode = $this->command->run($this->input, $this->output);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tester;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
/**
* Eases the testing of console applications.
*
* When testing an application, don't forget to disable the auto exit flag:
*
* $application = new Application();
* $application->setAutoExit(false);
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ApplicationTester
{
use TesterTrait;
private $application;
private $input;
private $statusCode;
public function __construct(Application $application)
{
$this->application = $application;
}
/**
* Executes the application.
*
* Available options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
* * capture_stderr_separately: Make output of stdOut and stdErr separately available
*
* @param array $input An array of arguments and options
* @param array $options An array of options
*
* @return int The command exit code
*/
public function run(array $input, $options = [])
{
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
$shellInteractive = getenv('SHELL_INTERACTIVE');
if ($this->inputs) {
$this->input->setStream(self::createStream($this->inputs));
putenv('SHELL_INTERACTIVE=1');
}
$this->initOutput($options);
$this->statusCode = $this->application->run($this->input, $this->output);
putenv($shellInteractive ? "SHELL_INTERACTIVE=$shellInteractive" : 'SHELL_INTERACTIVE');
return $this->statusCode;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tester;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\StreamOutput;
/**
* @author Amrouche Hamza <hamza.simperfit@gmail.com>
*/
trait TesterTrait
{
/** @var StreamOutput */
private $output;
private $inputs = [];
private $captureStreamsIndependently = false;
/**
* Gets the display returned by the last execution of the command or application.
*
* @param bool $normalize Whether to normalize end of lines to \n or not
*
* @return string The display
*/
public function getDisplay($normalize = false)
{
if (null === $this->output) {
throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?');
}
rewind($this->output->getStream());
$display = stream_get_contents($this->output->getStream());
if ($normalize) {
$display = str_replace(PHP_EOL, "\n", $display);
}
return $display;
}
/**
* Gets the output written to STDERR by the application.
*
* @param bool $normalize Whether to normalize end of lines to \n or not
*
* @return string
*/
public function getErrorOutput($normalize = false)
{
if (!$this->captureStreamsIndependently) {
throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.');
}
rewind($this->output->getErrorOutput()->getStream());
$display = stream_get_contents($this->output->getErrorOutput()->getStream());
if ($normalize) {
$display = str_replace(PHP_EOL, "\n", $display);
}
return $display;
}
/**
* Gets the input instance used by the last execution of the command or application.
*
* @return InputInterface The current input instance
*/
public function getInput()
{
return $this->input;
}
/**
* Gets the output instance used by the last execution of the command or application.
*
* @return OutputInterface The current output instance
*/
public function getOutput()
{
return $this->output;
}
/**
* Gets the status code returned by the last execution of the command or application.
*
* @return int The status code
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* Sets the user inputs.
*
* @param array $inputs An array of strings representing each input
* passed to the command input stream
*
* @return $this
*/
public function setInputs(array $inputs)
{
$this->inputs = $inputs;
return $this;
}
/**
* Initializes the output property.
*
* Available options:
*
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
* * capture_stderr_separately: Make output of stdOut and stdErr separately available
*/
private function initOutput(array $options)
{
$this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
if (!$this->captureStreamsIndependently) {
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity'])) {
$this->output->setVerbosity($options['verbosity']);
}
} else {
$this->output = new ConsoleOutput(
isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL,
isset($options['decorated']) ? $options['decorated'] : null
);
$errorOutput = new StreamOutput(fopen('php://memory', 'w', false));
$errorOutput->setFormatter($this->output->getFormatter());
$errorOutput->setVerbosity($this->output->getVerbosity());
$errorOutput->setDecorated($this->output->isDecorated());
$reflectedOutput = new \ReflectionObject($this->output);
$strErrProperty = $reflectedOutput->getProperty('stderr');
$strErrProperty->setAccessible(true);
$strErrProperty->setValue($this->output, $errorOutput);
$reflectedParent = $reflectedOutput->getParentClass();
$streamProperty = $reflectedParent->getProperty('stream');
$streamProperty->setAccessible(true);
$streamProperty->setValue($this->output, fopen('php://memory', 'w', false));
}
}
private static function createStream(array $inputs)
{
$stream = fopen('php://memory', 'r+', false);
foreach ($inputs as $input) {
fwrite($stream, $input.PHP_EOL);
}
rewind($stream);
return $stream;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR.
*
* This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR.
*
* $output = new ConsoleOutput();
*
* This is equivalent to:
*
* $output = new StreamOutput(fopen('php://stdout', 'w'));
* $stdErr = new StreamOutput(fopen('php://stderr', 'w'));
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
private $stderr;
private $consoleSectionOutputs = [];
/**
* @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param bool|null $decorated Whether to decorate messages (null for auto-guessing)
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*/
public function __construct(int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null)
{
parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);
$actualDecorated = $this->isDecorated();
$this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter());
if (null === $decorated) {
$this->setDecorated($actualDecorated && $this->stderr->isDecorated());
}
}
/**
* Creates a new output section.
*/
public function section(): ConsoleSectionOutput
{
return new ConsoleSectionOutput($this->getStream(), $this->consoleSectionOutputs, $this->getVerbosity(), $this->isDecorated(), $this->getFormatter());
}
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
parent::setDecorated($decorated);
$this->stderr->setDecorated($decorated);
}
/**
* {@inheritdoc}
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
parent::setFormatter($formatter);
$this->stderr->setFormatter($formatter);
}
/**
* {@inheritdoc}
*/
public function setVerbosity($level)
{
parent::setVerbosity($level);
$this->stderr->setVerbosity($level);
}
/**
* {@inheritdoc}
*/
public function getErrorOutput()
{
return $this->stderr;
}
/**
* {@inheritdoc}
*/
public function setErrorOutput(OutputInterface $error)
{
$this->stderr = $error;
}
/**
* Returns true if current environment supports writing console output to
* STDOUT.
*
* @return bool
*/
protected function hasStdoutSupport()
{
return false === $this->isRunningOS400();
}
/**
* Returns true if current environment supports writing console output to
* STDERR.
*
* @return bool
*/
protected function hasStderrSupport()
{
return false === $this->isRunningOS400();
}
/**
* Checks if current executing environment is IBM iSeries (OS400), which
* doesn't properly convert character-encodings between ASCII to EBCDIC.
*
* @return bool
*/
private function isRunningOS400()
{
$checks = [
\function_exists('php_uname') ? php_uname('s') : '',
getenv('OSTYPE'),
PHP_OS,
];
return false !== stripos(implode(';', $checks), 'OS400');
}
/**
* @return resource
*/
private function openOutputStream()
{
if (!$this->hasStdoutSupport()) {
return fopen('php://output', 'w');
}
return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
}
/**
* @return resource
*/
private function openErrorStream()
{
return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w');
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* NullOutput suppresses all output.
*
* $output = new NullOutput();
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Tobias Schultze <http://tobion.de>
*/
class NullOutput implements OutputInterface
{
/**
* {@inheritdoc}
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function getFormatter()
{
// to comply with the interface we must return a OutputFormatterInterface
return new OutputFormatter();
}
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function isDecorated()
{
return false;
}
/**
* {@inheritdoc}
*/
public function setVerbosity($level)
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function getVerbosity()
{
return self::VERBOSITY_QUIET;
}
/**
* {@inheritdoc}
*/
public function isQuiet()
{
return true;
}
/**
* {@inheritdoc}
*/
public function isVerbose()
{
return false;
}
/**
* {@inheritdoc}
*/
public function isVeryVerbose()
{
return false;
}
/**
* {@inheritdoc}
*/
public function isDebug()
{
return false;
}
/**
* {@inheritdoc}
*/
public function writeln($messages, $options = self::OUTPUT_NORMAL)
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
{
// do nothing
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* Base class for output classes.
*
* There are five levels of verbosity:
*
* * normal: no option passed (normal output)
* * verbose: -v (more output)
* * very verbose: -vv (highly extended output)
* * debug: -vvv (all debug output)
* * quiet: -q (no output)
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Output implements OutputInterface
{
private $verbosity;
private $formatter;
/**
* @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param bool $decorated Whether to decorate messages
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*/
public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null)
{
$this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity;
$this->formatter = $formatter ?: new OutputFormatter();
$this->formatter->setDecorated($decorated);
}
/**
* {@inheritdoc}
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
$this->formatter = $formatter;
}
/**
* {@inheritdoc}
*/
public function getFormatter()
{
return $this->formatter;
}
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
$this->formatter->setDecorated($decorated);
}
/**
* {@inheritdoc}
*/
public function isDecorated()
{
return $this->formatter->isDecorated();
}
/**
* {@inheritdoc}
*/
public function setVerbosity($level)
{
$this->verbosity = (int) $level;
}
/**
* {@inheritdoc}
*/
public function getVerbosity()
{
return $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function isQuiet()
{
return self::VERBOSITY_QUIET === $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function isVerbose()
{
return self::VERBOSITY_VERBOSE <= $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function isVeryVerbose()
{
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function isDebug()
{
return self::VERBOSITY_DEBUG <= $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function writeln($messages, $options = self::OUTPUT_NORMAL)
{
$this->write($messages, true, $options);
}
/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}
$types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN;
$type = $types & $options ?: self::OUTPUT_NORMAL;
$verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG;
$verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL;
if ($verbosity > $this->getVerbosity()) {
return;
}
foreach ($messages as $message) {
switch ($type) {
case OutputInterface::OUTPUT_NORMAL:
$message = $this->formatter->format($message);
break;
case OutputInterface::OUTPUT_RAW:
break;
case OutputInterface::OUTPUT_PLAIN:
$message = strip_tags($this->formatter->format($message));
break;
}
$this->doWrite($message, $newline);
}
}
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param bool $newline Whether to add a newline or not
*/
abstract protected function doWrite($message, $newline);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Output;
/**
* ConsoleOutputInterface is the interface implemented by ConsoleOutput class.
* This adds information about stderr and section output stream.
*
* @author Dariusz Górecki <darek.krk@gmail.com>
*
* @method ConsoleSectionOutput section() Creates a new output section
*/
interface ConsoleOutputInterface extends OutputInterface
{
/**
* Gets the OutputInterface for errors.
*
* @return OutputInterface
*/
public function getErrorOutput();
public function setErrorOutput(OutputInterface $error);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Output;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class BufferedOutput extends Output
{
private $buffer = '';
/**
* Empties buffer and returns its content.
*
* @return string
*/
public function fetch()
{
$content = $this->buffer;
$this->buffer = '';
return $content;
}
/**
* {@inheritdoc}
*/
protected function doWrite($message, $newline)
{
$this->buffer .= $message;
if ($newline) {
$this->buffer .= PHP_EOL;
}
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* StreamOutput writes the output to a given stream.
*
* Usage:
*
* $output = new StreamOutput(fopen('php://stdout', 'w'));
*
* As `StreamOutput` can use any stream, you can also use a file:
*
* $output = new StreamOutput(fopen('/path/to/output.log', 'a', false));
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class StreamOutput extends Output
{
private $stream;
/**
* @param resource $stream A stream resource
* @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param bool|null $decorated Whether to decorate messages (null for auto-guessing)
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*
* @throws InvalidArgumentException When first argument is not a real stream
*/
public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null)
{
if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
}
$this->stream = $stream;
if (null === $decorated) {
$decorated = $this->hasColorSupport();
}
parent::__construct($verbosity, $decorated, $formatter);
}
/**
* Gets the stream attached to this StreamOutput instance.
*
* @return resource A stream resource
*/
public function getStream()
{
return $this->stream;
}
/**
* {@inheritdoc}
*/
protected function doWrite($message, $newline)
{
if ($newline) {
$message .= PHP_EOL;
}
if (false === @fwrite($this->stream, $message)) {
// should never happen
throw new RuntimeException('Unable to write output.');
}
fflush($this->stream);
}
/**
* Returns true if the stream supports colorization.
*
* Colorization is disabled if not supported by the stream:
*
* This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo
* terminals via named pipes, so we can only check the environment.
*
* Reference: Composer\XdebugHandler\Process::supportsColor
* https://github.com/composer/xdebug-handler
*
* @return bool true if the stream supports colorization, false otherwise
*/
protected function hasColorSupport()
{
if ('Hyper' === getenv('TERM_PROGRAM')) {
return true;
}
if (\DIRECTORY_SEPARATOR === '\\') {
return (\function_exists('sapi_windows_vt100_support')
&& @sapi_windows_vt100_support($this->stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
}
if (\function_exists('stream_isatty')) {
return @stream_isatty($this->stream);
}
if (\function_exists('posix_isatty')) {
return @posix_isatty($this->stream);
}
$stat = @fstat($this->stream);
// Check if formatted mode is S_IFCHR
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Terminal;
/**
* @author Pierre du Plessis <pdples@gmail.com>
* @author Gabriel Ostrolucký <gabriel.ostrolucky@gmail.com>
*/
class ConsoleSectionOutput extends StreamOutput
{
private $content = [];
private $lines = 0;
private $sections;
private $terminal;
/**
* @param resource $stream
* @param ConsoleSectionOutput[] $sections
*/
public function __construct($stream, array &$sections, int $verbosity, bool $decorated, OutputFormatterInterface $formatter)
{
parent::__construct($stream, $verbosity, $decorated, $formatter);
array_unshift($sections, $this);
$this->sections = &$sections;
$this->terminal = new Terminal();
}
/**
* Clears previous output for this section.
*
* @param int $lines Number of lines to clear. If null, then the entire output of this section is cleared
*/
public function clear(int $lines = null)
{
if (empty($this->content) || !$this->isDecorated()) {
return;
}
if ($lines) {
array_splice($this->content, -($lines * 2)); // Multiply lines by 2 to cater for each new line added between content
} else {
$lines = $this->lines;
$this->content = [];
}
$this->lines -= $lines;
parent::doWrite($this->popStreamContentUntilCurrentSection($lines), false);
}
/**
* Overwrites the previous output with a new message.
*
* @param array|string $message
*/
public function overwrite($message)
{
$this->clear();
$this->writeln($message);
}
public function getContent(): string
{
return implode('', $this->content);
}
/**
* @internal
*/
public function addContent(string $input)
{
foreach (explode(PHP_EOL, $input) as $lineContent) {
$this->lines += ceil($this->getDisplayLength($lineContent) / $this->terminal->getWidth()) ?: 1;
$this->content[] = $lineContent;
$this->content[] = PHP_EOL;
}
}
/**
* {@inheritdoc}
*/
protected function doWrite($message, $newline)
{
if (!$this->isDecorated()) {
parent::doWrite($message, $newline);
return;
}
$erasedContent = $this->popStreamContentUntilCurrentSection();
$this->addContent($message);
parent::doWrite($message, true);
parent::doWrite($erasedContent, false);
}
/**
* At initial stage, cursor is at the end of stream output. This method makes cursor crawl upwards until it hits
* current section. Then it erases content it crawled through. Optionally, it erases part of current section too.
*/
private function popStreamContentUntilCurrentSection(int $numberOfLinesToClearFromCurrentSection = 0): string
{
$numberOfLinesToClear = $numberOfLinesToClearFromCurrentSection;
$erasedContent = [];
foreach ($this->sections as $section) {
if ($section === $this) {
break;
}
$numberOfLinesToClear += $section->lines;
$erasedContent[] = $section->getContent();
}
if ($numberOfLinesToClear > 0) {
// move cursor up n lines
parent::doWrite(sprintf("\x1b[%dA", $numberOfLinesToClear), false);
// erase to end of screen
parent::doWrite("\x1b[0J", false);
}
return implode('', array_reverse($erasedContent));
}
private function getDisplayLength(string $text): string
{
return Helper::strlenWithoutDecoration($this->getFormatter(), str_replace("\t", ' ', $text));
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* OutputInterface is the interface implemented by all Output classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface OutputInterface
{
const VERBOSITY_QUIET = 16;
const VERBOSITY_NORMAL = 32;
const VERBOSITY_VERBOSE = 64;
const VERBOSITY_VERY_VERBOSE = 128;
const VERBOSITY_DEBUG = 256;
const OUTPUT_NORMAL = 1;
const OUTPUT_RAW = 2;
const OUTPUT_PLAIN = 4;
/**
* Writes a message to the output.
*
* @param string|iterable $messages The message as an iterable of strings or a single string
* @param bool $newline Whether to add a newline
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
*/
public function write($messages, $newline = false, $options = 0);
/**
* Writes a message to the output and adds a newline at the end.
*
* @param string|iterable $messages The message as an iterable of strings or a single string
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
*/
public function writeln($messages, $options = 0);
/**
* Sets the verbosity of the output.
*
* @param int $level The level of verbosity (one of the VERBOSITY constants)
*/
public function setVerbosity($level);
/**
* Gets the current verbosity of the output.
*
* @return int The current level of verbosity (one of the VERBOSITY constants)
*/
public function getVerbosity();
/**
* Returns whether verbosity is quiet (-q).
*
* @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise
*/
public function isQuiet();
/**
* Returns whether verbosity is verbose (-v).
*
* @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise
*/
public function isVerbose();
/**
* Returns whether verbosity is very verbose (-vv).
*
* @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise
*/
public function isVeryVerbose();
/**
* Returns whether verbosity is debug (-vvv).
*
* @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise
*/
public function isDebug();
/**
* Sets the decorated flag.
*
* @param bool $decorated Whether to decorate the messages
*/
public function setDecorated($decorated);
/**
* Gets the decorated flag.
*
* @return bool true if the output will decorate messages, false otherwise
*/
public function isDecorated();
public function setFormatter(OutputFormatterInterface $formatter);
/**
* Returns current output formatter instance.
*
* @return OutputFormatterInterface
*/
public function getFormatter();
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Lock\Factory;
use Symfony\Component\Lock\Lock;
use Symfony\Component\Lock\Store\FlockStore;
use Symfony\Component\Lock\Store\SemaphoreStore;
/**
* Basic lock feature for commands.
*
* @author Geoffrey Brier <geoffrey.brier@gmail.com>
*/
trait LockableTrait
{
/** @var Lock */
private $lock;
/**
* Locks a command.
*
* @return bool
*/
private function lock($name = null, $blocking = false)
{
if (!class_exists(SemaphoreStore::class)) {
throw new LogicException('To enable the locking feature you must install the symfony/lock component.');
}
if (null !== $this->lock) {
throw new LogicException('A lock is already in place.');
}
if (SemaphoreStore::isSupported()) {
$store = new SemaphoreStore();
} else {
$store = new FlockStore();
}
$this->lock = (new Factory($store))->createLock($name ?: $this->getName());
if (!$this->lock->acquire($blocking)) {
$this->lock = null;
return false;
}
return true;
}
/**
* Releases the command lock if there is one.
*/
private function release()
{
if ($this->lock) {
$this->lock->release();
$this->lock = null;
}
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Base class for all commands.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Command
{
/**
* @var string|null The default command name
*/
protected static $defaultName;
private $application;
private $name;
private $processTitle;
private $aliases = [];
private $definition;
private $hidden = false;
private $help = '';
private $description = '';
private $ignoreValidationErrors = false;
private $applicationDefinitionMerged = false;
private $applicationDefinitionMergedWithArgs = false;
private $code;
private $synopsis = [];
private $usages = [];
private $helperSet;
/**
* @return string|null The default command name or null when no default name is set
*/
public static function getDefaultName()
{
$class = \get_called_class();
$r = new \ReflectionProperty($class, 'defaultName');
return $class === $r->class ? static::$defaultName : null;
}
/**
* @param string|null $name The name of the command; passing null means it must be set in configure()
*
* @throws LogicException When the command name is empty
*/
public function __construct(string $name = null)
{
$this->definition = new InputDefinition();
if (null !== $name || null !== $name = static::getDefaultName()) {
$this->setName($name);
}
$this->configure();
}
/**
* Ignores validation errors.
*
* This is mainly useful for the help command.
*/
public function ignoreValidationErrors()
{
$this->ignoreValidationErrors = true;
}
public function setApplication(Application $application = null)
{
$this->application = $application;
if ($application) {
$this->setHelperSet($application->getHelperSet());
} else {
$this->helperSet = null;
}
}
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
}
/**
* Gets the helper set.
*
* @return HelperSet|null A HelperSet instance
*/
public function getHelperSet()
{
return $this->helperSet;
}
/**
* Gets the application instance for this command.
*
* @return Application|null An Application instance
*/
public function getApplication()
{
return $this->application;
}
/**
* Checks whether the command is enabled or not in the current environment.
*
* Override this to check for x or y and return false if the command can not
* run properly under the current conditions.
*
* @return bool
*/
public function isEnabled()
{
return true;
}
/**
* Configures the current command.
*/
protected function configure()
{
}
/**
* Executes the current command.
*
* This method is not abstract because you can use this class
* as a concrete class. In this case, instead of defining the
* execute() method, you set the code to execute by passing
* a Closure to the setCode() method.
*
* @return int|null null or 0 if everything went fine, or an error code
*
* @throws LogicException When this abstract method is not implemented
*
* @see setCode()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
throw new LogicException('You must override the execute() method in the concrete command class.');
}
/**
* Interacts with the user.
*
* This method is executed before the InputDefinition is validated.
* This means that this is the only place where the command can
* interactively ask for values of missing required arguments.
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
}
/**
* Initializes the command after the input has been bound and before the input
* is validated.
*
* This is mainly useful when a lot of commands extends one main command
* where some things need to be initialized based on the input arguments and options.
*
* @see InputInterface::bind()
* @see InputInterface::validate()
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
}
/**
* Runs the command.
*
* The code to execute is either defined directly with the
* setCode() method or by overriding the execute() method
* in a sub-class.
*
* @return int The command exit code
*
* @throws \Exception When binding input fails. Bypass this by calling {@link ignoreValidationErrors()}.
*
* @see setCode()
* @see execute()
*/
public function run(InputInterface $input, OutputInterface $output)
{
// force the creation of the synopsis before the merge with the app definition
$this->getSynopsis(true);
$this->getSynopsis(false);
// add the application arguments and options
$this->mergeApplicationDefinition();
// bind the input against the command specific arguments/options
try {
$input->bind($this->definition);
} catch (ExceptionInterface $e) {
if (!$this->ignoreValidationErrors) {
throw $e;
}
}
$this->initialize($input, $output);
if (null !== $this->processTitle) {
if (\function_exists('cli_set_process_title')) {
if (!@cli_set_process_title($this->processTitle)) {
if ('Darwin' === PHP_OS) {
$output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
} else {
cli_set_process_title($this->processTitle);
}
}
} elseif (\function_exists('setproctitle')) {
setproctitle($this->processTitle);
} elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
$output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
}
}
if ($input->isInteractive()) {
$this->interact($input, $output);
}
// The command name argument is often omitted when a command is executed directly with its run() method.
// It would fail the validation if we didn't make sure the command argument is present,
// since it's required by the application.
if ($input->hasArgument('command') && null === $input->getArgument('command')) {
$input->setArgument('command', $this->getName());
}
$input->validate();
if ($this->code) {
$statusCode = ($this->code)($input, $output);
} else {
$statusCode = $this->execute($input, $output);
}
return is_numeric($statusCode) ? (int) $statusCode : 0;
}
/**
* Sets the code to execute when running this command.
*
* If this method is used, it overrides the code defined
* in the execute() method.
*
* @param callable $code A callable(InputInterface $input, OutputInterface $output)
*
* @return $this
*
* @throws InvalidArgumentException
*
* @see execute()
*/
public function setCode(callable $code)
{
if ($code instanceof \Closure) {
$r = new \ReflectionFunction($code);
if (null === $r->getClosureThis()) {
$code = \Closure::bind($code, $this);
}
}
$this->code = $code;
return $this;
}
/**
* Merges the application definition with the command definition.
*
* This method is not part of public API and should not be used directly.
*
* @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
*/
public function mergeApplicationDefinition($mergeArgs = true)
{
if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
return;
}
$this->definition->addOptions($this->application->getDefinition()->getOptions());
$this->applicationDefinitionMerged = true;
if ($mergeArgs) {
$currentArguments = $this->definition->getArguments();
$this->definition->setArguments($this->application->getDefinition()->getArguments());
$this->definition->addArguments($currentArguments);
$this->applicationDefinitionMergedWithArgs = true;
}
}
/**
* Sets an array of argument and option instances.
*
* @param array|InputDefinition $definition An array of argument and option instances or a definition instance
*
* @return $this
*/
public function setDefinition($definition)
{
if ($definition instanceof InputDefinition) {
$this->definition = $definition;
} else {
$this->definition->setDefinition($definition);
}
$this->applicationDefinitionMerged = false;
return $this;
}
/**
* Gets the InputDefinition attached to this Command.
*
* @return InputDefinition An InputDefinition instance
*/
public function getDefinition()
{
if (null === $this->definition) {
throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', \get_class($this)));
}
return $this->definition;
}
/**
* Gets the InputDefinition to be used to create representations of this Command.
*
* Can be overridden to provide the original command representation when it would otherwise
* be changed by merging with the application InputDefinition.
*
* This method is not part of public API and should not be used directly.
*
* @return InputDefinition An InputDefinition instance
*/
public function getNativeDefinition()
{
return $this->getDefinition();
}
/**
* Adds an argument.
*
* @param string $name The argument name
* @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
* @param string $description A description text
* @param string|string[]|null $default The default value (for InputArgument::OPTIONAL mode only)
*
* @throws InvalidArgumentException When argument mode is not valid
*
* @return $this
*/
public function addArgument($name, $mode = null, $description = '', $default = null)
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
return $this;
}
/**
* Adds an option.
*
* @param string $name The option name
* @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @param int|null $mode The option mode: One of the InputOption::VALUE_* constants
* @param string $description A description text
* @param string|string[]|int|bool|null $default The default value (must be null for InputOption::VALUE_NONE)
*
* @throws InvalidArgumentException If option mode is invalid or incompatible
*
* @return $this
*/
public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
return $this;
}
/**
* Sets the name of the command.
*
* This method can set both the namespace and the name if
* you separate them by a colon (:)
*
* $command->setName('foo:bar');
*
* @param string $name The command name
*
* @return $this
*
* @throws InvalidArgumentException When the name is invalid
*/
public function setName($name)
{
$this->validateName($name);
$this->name = $name;
return $this;
}
/**
* Sets the process title of the command.
*
* This feature should be used only when creating a long process command,
* like a daemon.
*
* PHP 5.5+ or the proctitle PECL library is required
*
* @param string $title The process title
*
* @return $this
*/
public function setProcessTitle($title)
{
$this->processTitle = $title;
return $this;
}
/**
* Returns the command name.
*
* @return string|null
*/
public function getName()
{
return $this->name;
}
/**
* @param bool $hidden Whether or not the command should be hidden from the list of commands
*
* @return Command The current instance
*/
public function setHidden($hidden)
{
$this->hidden = (bool) $hidden;
return $this;
}
/**
* @return bool whether the command should be publicly shown or not
*/
public function isHidden()
{
return $this->hidden;
}
/**
* Sets the description for the command.
*
* @param string $description The description for the command
*
* @return $this
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Returns the description for the command.
*
* @return string The description for the command
*/
public function getDescription()
{
return $this->description;
}
/**
* Sets the help for the command.
*
* @param string $help The help for the command
*
* @return $this
*/
public function setHelp($help)
{
$this->help = $help;
return $this;
}
/**
* Returns the help for the command.
*
* @return string The help for the command
*/
public function getHelp()
{
return $this->help;
}
/**
* Returns the processed help for the command replacing the %command.name% and
* %command.full_name% patterns with the real values dynamically.
*
* @return string The processed help for the command
*/
public function getProcessedHelp()
{
$name = $this->name;
$isSingleCommand = $this->application && $this->application->isSingleCommand();
$placeholders = [
'%command.name%',
'%command.full_name%',
];
$replacements = [
$name,
$isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name,
];
return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription());
}
/**
* Sets the aliases for the command.
*
* @param string[] $aliases An array of aliases for the command
*
* @return $this
*
* @throws InvalidArgumentException When an alias is invalid
*/
public function setAliases($aliases)
{
if (!\is_array($aliases) && !$aliases instanceof \Traversable) {
throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
}
foreach ($aliases as $alias) {
$this->validateName($alias);
}
$this->aliases = $aliases;
return $this;
}
/**
* Returns the aliases for the command.
*
* @return array An array of aliases for the command
*/
public function getAliases()
{
return $this->aliases;
}
/**
* Returns the synopsis for the command.
*
* @param bool $short Whether to show the short version of the synopsis (with options folded) or not
*
* @return string The synopsis
*/
public function getSynopsis($short = false)
{
$key = $short ? 'short' : 'long';
if (!isset($this->synopsis[$key])) {
$this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
}
return $this->synopsis[$key];
}
/**
* Add a command usage example.
*
* @param string $usage The usage, it'll be prefixed with the command name
*
* @return $this
*/
public function addUsage($usage)
{
if (0 !== strpos($usage, $this->name)) {
$usage = sprintf('%s %s', $this->name, $usage);
}
$this->usages[] = $usage;
return $this;
}
/**
* Returns alternative usages of the command.
*
* @return array
*/
public function getUsages()
{
return $this->usages;
}
/**
* Gets a helper instance by name.
*
* @param string $name The helper name
*
* @return mixed The helper value
*
* @throws LogicException if no HelperSet is defined
* @throws InvalidArgumentException if the helper is not defined
*/
public function getHelper($name)
{
if (null === $this->helperSet) {
throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name));
}
return $this->helperSet->get($name);
}
/**
* Validates a command name.
*
* It must be non-empty and parts can optionally be separated by ":".
*
* @throws InvalidArgumentException When the name is invalid
*/
private function validateName(string $name)
{
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
}
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* ListCommand displays the list of all available commands for the application.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ListCommand extends Command
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('list')
->setDefinition($this->createDefinition())
->setDescription('Lists commands')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command lists all commands:
<info>php %command.full_name%</info>
You can also display the commands for a specific namespace:
<info>php %command.full_name% test</info>
You can also output the information in other formats by using the <comment>--format</comment> option:
<info>php %command.full_name% --format=xml</info>
It's also possible to get raw list of commands (useful for embedding command runner):
<info>php %command.full_name% --raw</info>
EOF
)
;
}
/**
* {@inheritdoc}
*/
public function getNativeDefinition()
{
return $this->createDefinition();
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$helper = new DescriptorHelper();
$helper->describe($output, $this->getApplication(), [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'namespace' => $input->getArgument('namespace'),
]);
}
/**
* {@inheritdoc}
*/
private function createDefinition()
{
return new InputDefinition([
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
]);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Command;
use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* HelpCommand displays the help for a given command.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class HelpCommand extends Command
{
private $command;
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->ignoreValidationErrors();
$this
->setName('help')
->setDefinition([
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
])
->setDescription('Displays help for a command')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command displays help for a given command:
<info>php %command.full_name% list</info>
You can also output the help in other formats by using the <comment>--format</comment> option:
<info>php %command.full_name% --format=xml list</info>
To display the list of available commands, please use the <info>list</info> command.
EOF
)
;
}
public function setCommand(Command $command)
{
$this->command = $command;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if (null === $this->command) {
$this->command = $this->getApplication()->find($input->getArgument('command_name'));
}
$helper = new DescriptorHelper();
$helper->describe($output, $this->command, [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
]);
$this->command = null;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console;
/**
* Contains all events dispatched by an Application.
*
* @author Francesco Levorato <git@flevour.net>
*/
final class ConsoleEvents
{
/**
* The COMMAND event allows you to attach listeners before any command is
* executed by the console. It also allows you to modify the command, input and output
* before they are handled to the command.
*
* @Event("Symfony\Component\Console\Event\ConsoleCommandEvent")
*/
const COMMAND = 'console.command';
/**
* The TERMINATE event allows you to attach listeners after a command is
* executed by the console.
*
* @Event("Symfony\Component\Console\Event\ConsoleTerminateEvent")
*/
const TERMINATE = 'console.terminate';
/**
* The ERROR event occurs when an uncaught exception or error appears.
*
* This event allows you to deal with the exception/error or
* to modify the thrown exception.
*
* @Event("Symfony\Component\Console\Event\ConsoleErrorEvent")
*/
const ERROR = 'console.error';
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* HelperSet represents a set of helpers to be used with a command.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class HelperSet implements \IteratorAggregate
{
/**
* @var Helper[]
*/
private $helpers = [];
private $command;
/**
* @param Helper[] $helpers An array of helper
*/
public function __construct(array $helpers = [])
{
foreach ($helpers as $alias => $helper) {
$this->set($helper, \is_int($alias) ? null : $alias);
}
}
/**
* Sets a helper.
*
* @param HelperInterface $helper The helper instance
* @param string $alias An alias
*/
public function set(HelperInterface $helper, $alias = null)
{
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias) {
$this->helpers[$alias] = $helper;
}
$helper->setHelperSet($this);
}
/**
* Returns true if the helper if defined.
*
* @param string $name The helper name
*
* @return bool true if the helper is defined, false otherwise
*/
public function has($name)
{
return isset($this->helpers[$name]);
}
/**
* Gets a helper value.
*
* @param string $name The helper name
*
* @return HelperInterface The helper instance
*
* @throws InvalidArgumentException if the helper is not defined
*/
public function get($name)
{
if (!$this->has($name)) {
throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}
return $this->helpers[$name];
}
public function setCommand(Command $command = null)
{
$this->command = $command;
}
/**
* Gets the command associated with this helper set.
*
* @return Command A Command instance
*/
public function getCommand()
{
return $this->command;
}
/**
* @return Helper[]
*/
public function getIterator()
{
return new \ArrayIterator($this->helpers);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Descriptor\JsonDescriptor;
use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Output\OutputInterface;
/**
* This class adds helper method to describe objects in various formats.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class DescriptorHelper extends Helper
{
/**
* @var DescriptorInterface[]
*/
private $descriptors = [];
public function __construct()
{
$this
->register('txt', new TextDescriptor())
->register('xml', new XmlDescriptor())
->register('json', new JsonDescriptor())
->register('md', new MarkdownDescriptor())
;
}
/**
* Describes an object if supported.
*
* Available options are:
* * format: string, the output format name
* * raw_text: boolean, sets output type as raw
*
* @param object $object
*
* @throws InvalidArgumentException when the given format is not supported
*/
public function describe(OutputInterface $output, $object, array $options = [])
{
$options = array_merge([
'raw_text' => false,
'format' => 'txt',
], $options);
if (!isset($this->descriptors[$options['format']])) {
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
}
$descriptor = $this->descriptors[$options['format']];
$descriptor->describe($output, $object, $options);
}
/**
* Registers a descriptor.
*
* @param string $format
*
* @return $this
*/
public function register($format, DescriptorInterface $descriptor)
{
$this->descriptors[$format] = $descriptor;
return $this;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'descriptor';
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
/**
* Marks a row as being a separator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TableSeparator extends TableCell
{
public function __construct(array $options = [])
{
parent::__construct('', $options);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Defines the styles for a Table.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Саша Стаменковић <umpirsky@gmail.com>
* @author Dany Maillard <danymaillard93b@gmail.com>
*/
class TableStyle
{
private $paddingChar = ' ';
private $horizontalOutsideBorderChar = '-';
private $horizontalInsideBorderChar = '-';
private $verticalOutsideBorderChar = '|';
private $verticalInsideBorderChar = '|';
private $crossingChar = '+';
private $crossingTopRightChar = '+';
private $crossingTopMidChar = '+';
private $crossingTopLeftChar = '+';
private $crossingMidRightChar = '+';
private $crossingBottomRightChar = '+';
private $crossingBottomMidChar = '+';
private $crossingBottomLeftChar = '+';
private $crossingMidLeftChar = '+';
private $crossingTopLeftBottomChar = '+';
private $crossingTopMidBottomChar = '+';
private $crossingTopRightBottomChar = '+';
private $headerTitleFormat = '<fg=black;bg=white;options=bold> %s </>';
private $footerTitleFormat = '<fg=black;bg=white;options=bold> %s </>';
private $cellHeaderFormat = '<info>%s</info>';
private $cellRowFormat = '%s';
private $cellRowContentFormat = ' %s ';
private $borderFormat = '%s';
private $padType = STR_PAD_RIGHT;
/**
* Sets padding character, used for cell padding.
*
* @param string $paddingChar
*
* @return $this
*/
public function setPaddingChar($paddingChar)
{
if (!$paddingChar) {
throw new LogicException('The padding char must not be empty');
}
$this->paddingChar = $paddingChar;
return $this;
}
/**
* Gets padding character, used for cell padding.
*
* @return string
*/
public function getPaddingChar()
{
return $this->paddingChar;
}
/**
* Sets horizontal border characters.
*
* <code>
* ╔═══════════════╤══════════════════════════╤══════════════════╗
* 1 ISBN 2 Title │ Author ║
* ╠═══════════════╪══════════════════════════╪══════════════════╣
* ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
* ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
* ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
* ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
* ╚═══════════════╧══════════════════════════╧══════════════════╝
* </code>
*
* @param string $outside Outside border char (see #1 of example)
* @param string|null $inside Inside border char (see #2 of example), equals $outside if null
*/
public function setHorizontalBorderChars(string $outside, string $inside = null): self
{
$this->horizontalOutsideBorderChar = $outside;
$this->horizontalInsideBorderChar = $inside ?? $outside;
return $this;
}
/**
* Sets horizontal border character.
*
* @param string $horizontalBorderChar
*
* @return $this
*
* @deprecated since Symfony 4.1, use {@link setHorizontalBorderChars()} instead.
*/
public function setHorizontalBorderChar($horizontalBorderChar)
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setHorizontalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
return $this->setHorizontalBorderChars($horizontalBorderChar, $horizontalBorderChar);
}
/**
* Gets horizontal border character.
*
* @return string
*
* @deprecated since Symfony 4.1, use {@link getBorderChars()} instead.
*/
public function getHorizontalBorderChar()
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
return $this->horizontalOutsideBorderChar;
}
/**
* Sets vertical border characters.
*
* <code>
* ╔═══════════════╤══════════════════════════╤══════════════════╗
* ║ ISBN │ Title │ Author ║
* ╠═══════1═══════╪══════════════════════════╪══════════════════╣
* ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
* ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
* ╟───────2───────┼──────────────────────────┼──────────────────╢
* ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
* ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
* ╚═══════════════╧══════════════════════════╧══════════════════╝
* </code>
*
* @param string $outside Outside border char (see #1 of example)
* @param string|null $inside Inside border char (see #2 of example), equals $outside if null
*/
public function setVerticalBorderChars(string $outside, string $inside = null): self
{
$this->verticalOutsideBorderChar = $outside;
$this->verticalInsideBorderChar = $inside ?? $outside;
return $this;
}
/**
* Sets vertical border character.
*
* @param string $verticalBorderChar
*
* @return $this
*
* @deprecated since Symfony 4.1, use {@link setVerticalBorderChars()} instead.
*/
public function setVerticalBorderChar($verticalBorderChar)
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setVerticalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
return $this->setVerticalBorderChars($verticalBorderChar, $verticalBorderChar);
}
/**
* Gets vertical border character.
*
* @return string
*
* @deprecated since Symfony 4.1, use {@link getBorderChars()} instead.
*/
public function getVerticalBorderChar()
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
return $this->verticalOutsideBorderChar;
}
/**
* Gets border characters.
*
* @internal
*/
public function getBorderChars()
{
return [
$this->horizontalOutsideBorderChar,
$this->verticalOutsideBorderChar,
$this->horizontalInsideBorderChar,
$this->verticalInsideBorderChar,
];
}
/**
* Sets crossing characters.
*
* Example:
* <code>
* 1═══════════════2══════════════════════════2══════════════════3
* ║ ISBN │ Title │ Author ║
* 8'══════════════0'═════════════════════════0'═════════════════4'
* ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
* ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
* 8───────────────0──────────────────────────0──────────────────4
* ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
* ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
* 7═══════════════6══════════════════════════6══════════════════5
* </code>
*
* @param string $cross Crossing char (see #0 of example)
* @param string $topLeft Top left char (see #1 of example)
* @param string $topMid Top mid char (see #2 of example)
* @param string $topRight Top right char (see #3 of example)
* @param string $midRight Mid right char (see #4 of example)
* @param string $bottomRight Bottom right char (see #5 of example)
* @param string $bottomMid Bottom mid char (see #6 of example)
* @param string $bottomLeft Bottom left char (see #7 of example)
* @param string $midLeft Mid left char (see #8 of example)
* @param string|null $topLeftBottom Top left bottom char (see #8' of example), equals to $midLeft if null
* @param string|null $topMidBottom Top mid bottom char (see #0' of example), equals to $cross if null
* @param string|null $topRightBottom Top right bottom char (see #4' of example), equals to $midRight if null
*/
public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, string $topLeftBottom = null, string $topMidBottom = null, string $topRightBottom = null): self
{
$this->crossingChar = $cross;
$this->crossingTopLeftChar = $topLeft;
$this->crossingTopMidChar = $topMid;
$this->crossingTopRightChar = $topRight;
$this->crossingMidRightChar = $midRight;
$this->crossingBottomRightChar = $bottomRight;
$this->crossingBottomMidChar = $bottomMid;
$this->crossingBottomLeftChar = $bottomLeft;
$this->crossingMidLeftChar = $midLeft;
$this->crossingTopLeftBottomChar = $topLeftBottom ?? $midLeft;
$this->crossingTopMidBottomChar = $topMidBottom ?? $cross;
$this->crossingTopRightBottomChar = $topRightBottom ?? $midRight;
return $this;
}
/**
* Sets default crossing character used for each cross.
*
* @see {@link setCrossingChars()} for setting each crossing individually.
*/
public function setDefaultCrossingChar(string $char): self
{
return $this->setCrossingChars($char, $char, $char, $char, $char, $char, $char, $char, $char);
}
/**
* Sets crossing character.
*
* @param string $crossingChar
*
* @return $this
*
* @deprecated since Symfony 4.1. Use {@link setDefaultCrossingChar()} instead.
*/
public function setCrossingChar($crossingChar)
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use setDefaultCrossingChar() instead.', __METHOD__), E_USER_DEPRECATED);
return $this->setDefaultCrossingChar($crossingChar);
}
/**
* Gets crossing character.
*
* @return string
*/
public function getCrossingChar()
{
return $this->crossingChar;
}
/**
* Gets crossing characters.
*
* @internal
*/
public function getCrossingChars(): array
{
return [
$this->crossingChar,
$this->crossingTopLeftChar,
$this->crossingTopMidChar,
$this->crossingTopRightChar,
$this->crossingMidRightChar,
$this->crossingBottomRightChar,
$this->crossingBottomMidChar,
$this->crossingBottomLeftChar,
$this->crossingMidLeftChar,
$this->crossingTopLeftBottomChar,
$this->crossingTopMidBottomChar,
$this->crossingTopRightBottomChar,
];
}
/**
* Sets header cell format.
*
* @param string $cellHeaderFormat
*
* @return $this
*/
public function setCellHeaderFormat($cellHeaderFormat)
{
$this->cellHeaderFormat = $cellHeaderFormat;
return $this;
}
/**
* Gets header cell format.
*
* @return string
*/
public function getCellHeaderFormat()
{
return $this->cellHeaderFormat;
}
/**
* Sets row cell format.
*
* @param string $cellRowFormat
*
* @return $this
*/
public function setCellRowFormat($cellRowFormat)
{
$this->cellRowFormat = $cellRowFormat;
return $this;
}
/**
* Gets row cell format.
*
* @return string
*/
public function getCellRowFormat()
{
return $this->cellRowFormat;
}
/**
* Sets row cell content format.
*
* @param string $cellRowContentFormat
*
* @return $this
*/
public function setCellRowContentFormat($cellRowContentFormat)
{
$this->cellRowContentFormat = $cellRowContentFormat;
return $this;
}
/**
* Gets row cell content format.
*
* @return string
*/
public function getCellRowContentFormat()
{
return $this->cellRowContentFormat;
}
/**
* Sets table border format.
*
* @param string $borderFormat
*
* @return $this
*/
public function setBorderFormat($borderFormat)
{
$this->borderFormat = $borderFormat;
return $this;
}
/**
* Gets table border format.
*
* @return string
*/
public function getBorderFormat()
{
return $this->borderFormat;
}
/**
* Sets cell padding type.
*
* @param int $padType STR_PAD_*
*
* @return $this
*/
public function setPadType($padType)
{
if (!\in_array($padType, [STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH], true)) {
throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
}
$this->padType = $padType;
return $this;
}
/**
* Gets cell padding type.
*
* @return int
*/
public function getPadType()
{
return $this->padType;
}
public function getHeaderTitleFormat(): string
{
return $this->headerTitleFormat;
}
public function setHeaderTitleFormat(string $format): self
{
$this->headerTitleFormat = $format;
return $this;
}
public function getFooterTitleFormat(): string
{
return $this->footerTitleFormat;
}
public function setFooterTitleFormat(string $format): self
{
$this->footerTitleFormat = $format;
return $this;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Input\InputInterface;
/**
* An implementation of InputAwareInterface for Helpers.
*
* @author Wouter J <waldio.webdesign@gmail.com>
*/
abstract class InputAwareHelper extends Helper implements InputAwareInterface
{
protected $input;
/**
* {@inheritdoc}
*/
public function setInput(InputInterface $input)
{
$this->input = $input;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
/**
* Helps outputting debug information when running an external program from a command.
*
* An external program can be a Process, an HTTP request, or anything else.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DebugFormatterHelper extends Helper
{
private $colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'];
private $started = [];
private $count = -1;
/**
* Starts a debug formatting session.
*
* @param string $id The id of the formatting session
* @param string $message The message to display
* @param string $prefix The prefix to use
*
* @return string
*/
public function start($id, $message, $prefix = 'RUN')
{
$this->started[$id] = ['border' => ++$this->count % \count($this->colors)];
return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message);
}
/**
* Adds progress to a formatting session.
*
* @param string $id The id of the formatting session
* @param string $buffer The message to display
* @param bool $error Whether to consider the buffer as error
* @param string $prefix The prefix for output
* @param string $errorPrefix The prefix for error output
*
* @return string
*/
public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR')
{
$message = '';
if ($error) {
if (isset($this->started[$id]['out'])) {
$message .= "\n";
unset($this->started[$id]['out']);
}
if (!isset($this->started[$id]['err'])) {
$message .= sprintf('%s<bg=red;fg=white> %s </> ', $this->getBorder($id), $errorPrefix);
$this->started[$id]['err'] = true;
}
$message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer);
} else {
if (isset($this->started[$id]['err'])) {
$message .= "\n";
unset($this->started[$id]['err']);
}
if (!isset($this->started[$id]['out'])) {
$message .= sprintf('%s<bg=green;fg=white> %s </> ', $this->getBorder($id), $prefix);
$this->started[$id]['out'] = true;
}
$message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer);
}
return $message;
}
/**
* Stops a formatting session.
*
* @param string $id The id of the formatting session
* @param string $message The message to display
* @param bool $successful Whether to consider the result as success
* @param string $prefix The prefix for the end output
*
* @return string
*/
public function stop($id, $message, $successful, $prefix = 'RES')
{
$trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';
if ($successful) {
return sprintf("%s%s<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
}
$message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
unset($this->started[$id]['out'], $this->started[$id]['err']);
return $message;
}
/**
* @param string $id The id of the formatting session
*
* @return string
*/
private function getBorder($id)
{
return sprintf('<bg=%s> </>', $this->colors[$this->started[$id]['border']]);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'debug_formatter';
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Provides helpers to display a table.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Саша Стаменковић <umpirsky@gmail.com>
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
* @author Max Grigorian <maxakawizard@gmail.com>
* @author Dany Maillard <danymaillard93b@gmail.com>
*/
class Table
{
private const SEPARATOR_TOP = 0;
private const SEPARATOR_TOP_BOTTOM = 1;
private const SEPARATOR_MID = 2;
private const SEPARATOR_BOTTOM = 3;
private const BORDER_OUTSIDE = 0;
private const BORDER_INSIDE = 1;
private $headerTitle;
private $footerTitle;
/**
* Table headers.
*/
private $headers = [];
/**
* Table rows.
*/
private $rows = [];
/**
* Column widths cache.
*/
private $effectiveColumnWidths = [];
/**
* Number of columns cache.
*
* @var int
*/
private $numberOfColumns;
/**
* @var OutputInterface
*/
private $output;
/**
* @var TableStyle
*/
private $style;
/**
* @var array
*/
private $columnStyles = [];
/**
* User set column widths.
*
* @var array
*/
private $columnWidths = [];
private $columnMaxWidths = [];
private static $styles;
private $rendered = false;
public function __construct(OutputInterface $output)
{
$this->output = $output;
if (!self::$styles) {
self::$styles = self::initStyles();
}
$this->setStyle('default');
}
/**
* Sets a style definition.
*
* @param string $name The style name
* @param TableStyle $style A TableStyle instance
*/
public static function setStyleDefinition($name, TableStyle $style)
{
if (!self::$styles) {
self::$styles = self::initStyles();
}
self::$styles[$name] = $style;
}
/**
* Gets a style definition by name.
*
* @param string $name The style name
*
* @return TableStyle
*/
public static function getStyleDefinition($name)
{
if (!self::$styles) {
self::$styles = self::initStyles();
}
if (isset(self::$styles[$name])) {
return self::$styles[$name];
}
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
/**
* Sets table style.
*
* @param TableStyle|string $name The style name or a TableStyle instance
*
* @return $this
*/
public function setStyle($name)
{
$this->style = $this->resolveStyle($name);
return $this;
}
/**
* Gets the current table style.
*
* @return TableStyle
*/
public function getStyle()
{
return $this->style;
}
/**
* Sets table column style.
*
* @param int $columnIndex Column index
* @param TableStyle|string $name The style name or a TableStyle instance
*
* @return $this
*/
public function setColumnStyle($columnIndex, $name)
{
$columnIndex = (int) $columnIndex;
$this->columnStyles[$columnIndex] = $this->resolveStyle($name);
return $this;
}
/**
* Gets the current style for a column.
*
* If style was not set, it returns the global table style.
*
* @param int $columnIndex Column index
*
* @return TableStyle
*/
public function getColumnStyle($columnIndex)
{
return $this->columnStyles[$columnIndex] ?? $this->getStyle();
}
/**
* Sets the minimum width of a column.
*
* @param int $columnIndex Column index
* @param int $width Minimum column width in characters
*
* @return $this
*/
public function setColumnWidth($columnIndex, $width)
{
$this->columnWidths[(int) $columnIndex] = (int) $width;
return $this;
}
/**
* Sets the minimum width of all columns.
*
* @return $this
*/
public function setColumnWidths(array $widths)
{
$this->columnWidths = [];
foreach ($widths as $index => $width) {
$this->setColumnWidth($index, $width);
}
return $this;
}
/**
* Sets the maximum width of a column.
*
* Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while
* formatted strings are preserved.
*
* @return $this
*/
public function setColumnMaxWidth(int $columnIndex, int $width): self
{
if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, \get_class($this->output->getFormatter())));
}
$this->columnMaxWidths[$columnIndex] = $width;
return $this;
}
public function setHeaders(array $headers)
{
$headers = array_values($headers);
if (!empty($headers) && !\is_array($headers[0])) {
$headers = [$headers];
}
$this->headers = $headers;
return $this;
}
public function setRows(array $rows)
{
$this->rows = [];
return $this->addRows($rows);
}
public function addRows(array $rows)
{
foreach ($rows as $row) {
$this->addRow($row);
}
return $this;
}
public function addRow($row)
{
if ($row instanceof TableSeparator) {
$this->rows[] = $row;
return $this;
}
if (!\is_array($row)) {
throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
}
$this->rows[] = array_values($row);
return $this;
}
/**
* Adds a row to the table, and re-renders the table.
*/
public function appendRow($row): self
{
if (!$this->output instanceof ConsoleSectionOutput) {
throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__));
}
if ($this->rendered) {
$this->output->clear($this->calculateRowCount());
}
$this->addRow($row);
$this->render();
return $this;
}
public function setRow($column, array $row)
{
$this->rows[$column] = $row;
return $this;
}
public function setHeaderTitle(?string $title): self
{
$this->headerTitle = $title;
return $this;
}
public function setFooterTitle(?string $title): self
{
$this->footerTitle = $title;
return $this;
}
/**
* Renders table to output.
*
* Example:
*
* +---------------+-----------------------+------------------+
* | ISBN | Title | Author |
* +---------------+-----------------------+------------------+
* | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
* | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
* +---------------+-----------------------+------------------+
*/
public function render()
{
$rows = array_merge($this->headers, [$divider = new TableSeparator()], $this->rows);
$this->calculateNumberOfColumns($rows);
$rows = $this->buildTableRows($rows);
$this->calculateColumnsWidth($rows);
$isHeader = true;
$isFirstRow = false;
foreach ($rows as $row) {
if ($divider === $row) {
$isHeader = false;
$isFirstRow = true;
continue;
}
if ($row instanceof TableSeparator) {
$this->renderRowSeparator();
continue;
}
if (!$row) {
continue;
}
if ($isHeader || $isFirstRow) {
if ($isFirstRow) {
$this->renderRowSeparator(self::SEPARATOR_TOP_BOTTOM);
$isFirstRow = false;
} else {
$this->renderRowSeparator(self::SEPARATOR_TOP, $this->headerTitle, $this->style->getHeaderTitleFormat());
}
}
$this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
}
$this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat());
$this->cleanup();
$this->rendered = true;
}
/**
* Renders horizontal header separator.
*
* Example:
*
* +-----+-----------+-------+
*/
private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
{
if (0 === $count = $this->numberOfColumns) {
return;
}
$borders = $this->style->getBorderChars();
if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) {
return;
}
$crossings = $this->style->getCrossingChars();
if (self::SEPARATOR_MID === $type) {
list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
} elseif (self::SEPARATOR_TOP === $type) {
list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
} elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
} else {
list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
}
$markup = $leftChar;
for ($column = 0; $column < $count; ++$column) {
$markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]);
$markup .= $column === $count - 1 ? $rightChar : $midChar;
}
if (null !== $title) {
$titleLength = Helper::strlenWithoutDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title));
$markupLength = Helper::strlen($markup);
if ($titleLength > $limit = $markupLength - 4) {
$titleLength = $limit;
$formatLength = Helper::strlenWithoutDecoration($formatter, sprintf($titleFormat, ''));
$formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
}
$titleStart = ($markupLength - $titleLength) / 2;
if (false === mb_detect_encoding($markup, null, true)) {
$markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
} else {
$markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength);
}
}
$this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
}
/**
* Renders vertical column separator.
*/
private function renderColumnSeparator($type = self::BORDER_OUTSIDE)
{
$borders = $this->style->getBorderChars();
return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]);
}
/**
* Renders table row.
*
* Example:
*
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
*/
private function renderRow(array $row, string $cellFormat)
{
$rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
$columns = $this->getRowColumns($row);
$last = \count($columns) - 1;
foreach ($columns as $i => $column) {
$rowContent .= $this->renderCell($row, $column, $cellFormat);
$rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE);
}
$this->output->writeln($rowContent);
}
/**
* Renders table cell with padding.
*/
private function renderCell(array $row, int $column, string $cellFormat)
{
$cell = isset($row[$column]) ? $row[$column] : '';
$width = $this->effectiveColumnWidths[$column];
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
// add the width of the following columns(numbers of colspan).
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
$width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
}
}
// str_pad won't work properly with multi-byte strings, we need to fix the padding
if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
$width += \strlen($cell) - mb_strwidth($cell, $encoding);
}
$style = $this->getColumnStyle($column);
if ($cell instanceof TableSeparator) {
return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width));
}
$width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
$content = sprintf($style->getCellRowContentFormat(), $cell);
return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType()));
}
/**
* Calculate number of columns for this table.
*/
private function calculateNumberOfColumns($rows)
{
$columns = [0];
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
$columns[] = $this->getNumberOfColumns($row);
}
$this->numberOfColumns = max($columns);
}
private function buildTableRows($rows)
{
/** @var WrappableOutputFormatterInterface $formatter */
$formatter = $this->output->getFormatter();
$unmergedRows = [];
for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
$rows = $this->fillNextRows($rows, $rowKey);
// Remove any new line breaks and replace it with a new line
foreach ($rows[$rowKey] as $column => $cell) {
$colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
if (isset($this->columnMaxWidths[$column]) && Helper::strlenWithoutDecoration($formatter, $cell) > $this->columnMaxWidths[$column]) {
$cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
}
if (!strstr($cell, "\n")) {
continue;
}
$escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
$cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
foreach ($lines as $lineKey => $line) {
if ($colspan > 1) {
$line = new TableCell($line, ['colspan' => $colspan]);
}
if (0 === $lineKey) {
$rows[$rowKey][$column] = $line;
} else {
$unmergedRows[$rowKey][$lineKey][$column] = $line;
}
}
}
}
return new TableRows(function () use ($rows, $unmergedRows) {
foreach ($rows as $rowKey => $row) {
yield $this->fillCells($row);
if (isset($unmergedRows[$rowKey])) {
foreach ($unmergedRows[$rowKey] as $row) {
yield $row;
}
}
}
});
}
private function calculateRowCount(): int
{
$numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));
if ($this->headers) {
++$numberOfRows; // Add row for header separator
}
++$numberOfRows; // Add row for footer separator
return $numberOfRows;
}
/**
* fill rows that contains rowspan > 1.
*
* @throws InvalidArgumentException
*/
private function fillNextRows(array $rows, int $line): array
{
$unmergedRows = [];
foreach ($rows[$line] as $column => $cell) {
if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing __toString, %s given.', \gettype($cell)));
}
if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
$nbLines = $cell->getRowspan() - 1;
$lines = [$cell];
if (strstr($cell, "\n")) {
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
$nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
$rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan()]);
unset($lines[0]);
}
// create a two dimensional array (rowspan x colspan)
$unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
$value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : '';
$unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan()]);
if ($nbLines === $unmergedRowKey - $line) {
break;
}
}
}
}
foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
// we need to know if $unmergedRow will be merged or inserted into $rows
if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
foreach ($unmergedRow as $cellKey => $cell) {
// insert cell into row at cellKey position
array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
}
} else {
$row = $this->copyRow($rows, $unmergedRowKey - 1);
foreach ($unmergedRow as $column => $cell) {
if (!empty($cell)) {
$row[$column] = $unmergedRow[$column];
}
}
array_splice($rows, $unmergedRowKey, 0, [$row]);
}
}
return $rows;
}
/**
* fill cells for a row that contains colspan > 1.
*/
private function fillCells($row)
{
$newRow = [];
foreach ($row as $column => $cell) {
$newRow[] = $cell;
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
// insert empty value at column position
$newRow[] = '';
}
}
}
return $newRow ?: $row;
}
private function copyRow(array $rows, int $line): array
{
$row = $rows[$line];
foreach ($row as $cellKey => $cellValue) {
$row[$cellKey] = '';
if ($cellValue instanceof TableCell) {
$row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
}
}
return $row;
}
/**
* Gets number of columns by row.
*/
private function getNumberOfColumns(array $row): int
{
$columns = \count($row);
foreach ($row as $column) {
$columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
}
return $columns;
}
/**
* Gets list of columns for the given row.
*/
private function getRowColumns(array $row): array
{
$columns = range(0, $this->numberOfColumns - 1);
foreach ($row as $cellKey => $cell) {
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
// exclude grouped columns.
$columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
}
}
return $columns;
}
/**
* Calculates columns widths.
*/
private function calculateColumnsWidth(iterable $rows)
{
for ($column = 0; $column < $this->numberOfColumns; ++$column) {
$lengths = [];
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
foreach ($row as $i => $cell) {
if ($cell instanceof TableCell) {
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
$textLength = Helper::strlen($textContent);
if ($textLength > 0) {
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
foreach ($contentColumns as $position => $content) {
$row[$i + $position] = $content;
}
}
}
}
$lengths[] = $this->getCellWidth($row, $column);
}
$this->effectiveColumnWidths[$column] = max($lengths) + Helper::strlen($this->style->getCellRowContentFormat()) - 2;
}
}
private function getColumnSeparatorWidth(): int
{
return Helper::strlen(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3]));
}
private function getCellWidth(array $row, int $column): int
{
$cellWidth = 0;
if (isset($row[$column])) {
$cell = $row[$column];
$cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
}
$columnWidth = isset($this->columnWidths[$column]) ? $this->columnWidths[$column] : 0;
$cellWidth = max($cellWidth, $columnWidth);
return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
}
/**
* Called after rendering to cleanup cache data.
*/
private function cleanup()
{
$this->effectiveColumnWidths = [];
$this->numberOfColumns = null;
}
private static function initStyles()
{
$borderless = new TableStyle();
$borderless
->setHorizontalBorderChars('=')
->setVerticalBorderChars(' ')
->setDefaultCrossingChar(' ')
;
$compact = new TableStyle();
$compact
->setHorizontalBorderChars('')
->setVerticalBorderChars(' ')
->setDefaultCrossingChar('')
->setCellRowContentFormat('%s')
;
$styleGuide = new TableStyle();
$styleGuide
->setHorizontalBorderChars('-')
->setVerticalBorderChars(' ')
->setDefaultCrossingChar(' ')
->setCellHeaderFormat('%s')
;
$box = (new TableStyle())
->setHorizontalBorderChars('─')
->setVerticalBorderChars('│')
->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├')
;
$boxDouble = (new TableStyle())
->setHorizontalBorderChars('═', '─')
->setVerticalBorderChars('║', '│')
->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣')
;
return [
'default' => new TableStyle(),
'borderless' => $borderless,
'compact' => $compact,
'symfony-style-guide' => $styleGuide,
'box' => $box,
'box-double' => $boxDouble,
];
}
private function resolveStyle($name)
{
if ($name instanceof TableStyle) {
return $name;
}
if (isset(self::$styles[$name])) {
return self::$styles[$name];
}
throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* Helper is the base class for all helper classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Helper implements HelperInterface
{
protected $helperSet = null;
/**
* {@inheritdoc}
*/
public function setHelperSet(HelperSet $helperSet = null)
{
$this->helperSet = $helperSet;
}
/**
* {@inheritdoc}
*/
public function getHelperSet()
{
return $this->helperSet;
}
/**
* Returns the length of a string, using mb_strwidth if it is available.
*
* @param string $string The string to check its length
*
* @return int The length of the string
*/
public static function strlen($string)
{
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return \strlen($string);
}
return mb_strwidth($string, $encoding);
}
/**
* Returns the subset of a string, using mb_substr if it is available.
*
* @param string $string String to subset
* @param int $from Start offset
* @param int|null $length Length to read
*
* @return string The string subset
*/
public static function substr($string, $from, $length = null)
{
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return substr($string, $from, $length);
}
return mb_substr($string, $from, $length, $encoding);
}
public static function formatTime($secs)
{
static $timeFormats = [
[0, '< 1 sec'],
[1, '1 sec'],
[2, 'secs', 1],
[60, '1 min'],
[120, 'mins', 60],
[3600, '1 hr'],
[7200, 'hrs', 3600],
[86400, '1 day'],
[172800, 'days', 86400],
];
foreach ($timeFormats as $index => $format) {
if ($secs >= $format[0]) {
if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0])
|| $index == \count($timeFormats) - 1
) {
if (2 == \count($format)) {
return $format[1];
}
return floor($secs / $format[2]).' '.$format[1];
}
}
}
}
public static function formatMemory($memory)
{
if ($memory >= 1024 * 1024 * 1024) {
return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
}
if ($memory >= 1024 * 1024) {
return sprintf('%.1f MiB', $memory / 1024 / 1024);
}
if ($memory >= 1024) {
return sprintf('%d KiB', $memory / 1024);
}
return sprintf('%d B', $memory);
}
public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string)
{
return self::strlen(self::removeDecoration($formatter, $string));
}
public static function removeDecoration(OutputFormatterInterface $formatter, $string)
{
$isDecorated = $formatter->isDecorated();
$formatter->setDecorated(false);
// remove <...> formatting
$string = $formatter->format($string);
// remove already formatted characters
$string = preg_replace("/\033\[[^m]*m/", '', $string);
$formatter->setDecorated($isDecorated);
return $string;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/
class TableCell
{
private $value;
private $options = [
'rowspan' => 1,
'colspan' => 1,
];
public function __construct(string $value = '', array $options = [])
{
$this->value = $value;
// check option names
if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff)));
}
$this->options = array_merge($this->options, $options);
}
/**
* Returns the cell value.
*
* @return string
*/
public function __toString()
{
return $this->value;
}
/**
* Gets number of colspan.
*
* @return int
*/
public function getColspan()
{
return (int) $this->options['colspan'];
}
/**
* Gets number of rowspan.
*
* @return int
*/
public function getRowspan()
{
return (int) $this->options['rowspan'];
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatter;
/**
* The Formatter class provides helpers to format messages.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FormatterHelper extends Helper
{
/**
* Formats a message within a section.
*
* @param string $section The section name
* @param string $message The message
* @param string $style The style to apply to the section
*
* @return string The format section
*/
public function formatSection($section, $message, $style = 'info')
{
return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
}
/**
* Formats a message as a block of text.
*
* @param string|array $messages The message to write in the block
* @param string $style The style to apply to the whole block
* @param bool $large Whether to return a large block
*
* @return string The formatter message
*/
public function formatBlock($messages, $style, $large = false)
{
if (!\is_array($messages)) {
$messages = [$messages];
}
$len = 0;
$lines = [];
foreach ($messages as $message) {
$message = OutputFormatter::escape($message);
$lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
$len = max($this->strlen($message) + ($large ? 4 : 2), $len);
}
$messages = $large ? [str_repeat(' ', $len)] : [];
for ($i = 0; isset($lines[$i]); ++$i) {
$messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i]));
}
if ($large) {
$messages[] = str_repeat(' ', $len);
}
for ($i = 0; isset($messages[$i]); ++$i) {
$messages[$i] = sprintf('<%s>%s</%s>', $style, $messages[$i], $style);
}
return implode("\n", $messages);
}
/**
* Truncates a message to the given length.
*
* @param string $message
* @param int $length
* @param string $suffix
*
* @return string
*/
public function truncate($message, $length, $suffix = '...')
{
$computedLength = $length - $this->strlen($suffix);
if ($computedLength > $this->strlen($message)) {
return $message;
}
if (false === $encoding = mb_detect_encoding($message, null, true)) {
return substr($message, 0, $length).$suffix;
}
return mb_substr($message, 0, $length, $encoding).$suffix;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'formatter';
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
/**
* @author Roland Franssen <franssen.roland@gmail.com>
*/
final class Dumper
{
private $output;
private $dumper;
private $cloner;
private $handler;
public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
{
$this->output = $output;
$this->dumper = $dumper;
$this->cloner = $cloner;
if (class_exists(CliDumper::class)) {
$this->handler = function ($var): string {
$dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
$dumper->setColors($this->output->isDecorated());
return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true));
};
} else {
$this->handler = function ($var): string {
switch (true) {
case null === $var:
return 'null';
case true === $var:
return 'true';
case false === $var:
return 'false';
case \is_string($var):
return '"'.$var.'"';
default:
return rtrim(print_r($var, true));
}
};
}
}
public function __invoke($var): string
{
return ($this->handler)($var);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
/**
* HelperInterface is the interface all helpers must implement.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface HelperInterface
{
/**
* Sets the helper set associated with this helper.
*/
public function setHelperSet(HelperSet $helperSet = null);
/**
* Gets the helper set associated with this helper.
*
* @return HelperSet A HelperSet instance
*/
public function getHelperSet();
/**
* Returns the canonical name of this helper.
*
* @return string The canonical name
*/
public function getName();
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;
/**
* The ProcessHelper class provides helpers to run external processes.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since Symfony 4.2
*/
class ProcessHelper extends Helper
{
/**
* Runs an external process.
*
* @param OutputInterface $output An OutputInterface instance
* @param array|Process $cmd An instance of Process or an array of the command and arguments
* @param string|null $error An error message that must be displayed if something went wrong
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
* @param int $verbosity The threshold for verbosity
*
* @return Process The process that ran
*/
public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$formatter = $this->getHelperSet()->get('debug_formatter');
if ($cmd instanceof Process) {
$cmd = [$cmd];
}
if (!\is_array($cmd)) {
@trigger_error(sprintf('Passing a command as a string to "%s()" is deprecated since Symfony 4.2, pass it the command as an array of arguments instead.', __METHOD__), E_USER_DEPRECATED);
$cmd = [method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline($cmd) : new Process($cmd)];
}
if (\is_string($cmd[0] ?? null)) {
$process = new Process($cmd);
$cmd = [];
} elseif (($cmd[0] ?? null) instanceof Process) {
$process = $cmd[0];
unset($cmd[0]);
} else {
throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__));
}
if ($verbosity <= $output->getVerbosity()) {
$output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
}
if ($output->isDebug()) {
$callback = $this->wrapCallback($output, $process, $callback);
}
$process->run($callback, $cmd);
if ($verbosity <= $output->getVerbosity()) {
$message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
$output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
}
if (!$process->isSuccessful() && null !== $error) {
$output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
}
return $process;
}
/**
* Runs the process.
*
* This is identical to run() except that an exception is thrown if the process
* exits with a non-zero exit code.
*
* @param OutputInterface $output An OutputInterface instance
* @param string|Process $cmd An instance of Process or a command to run
* @param string|null $error An error message that must be displayed if something went wrong
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
*
* @return Process The process that ran
*
* @throws ProcessFailedException
*
* @see run()
*/
public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null)
{
$process = $this->run($output, $cmd, $error, $callback);
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
return $process;
}
/**
* Wraps a Process callback to add debugging output.
*
* @param OutputInterface $output An OutputInterface interface
* @param Process $process The Process
* @param callable|null $callback A PHP callable
*
* @return callable
*/
public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$formatter = $this->getHelperSet()->get('debug_formatter');
return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
$output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));
if (null !== $callback) {
$callback($type, $buffer);
}
};
}
private function escapeString($str)
{
return str_replace('<', '\\<', $str);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'process';
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
class ProgressIndicator
{
private $output;
private $startTime;
private $format;
private $message;
private $indicatorValues;
private $indicatorCurrent;
private $indicatorChangeInterval;
private $indicatorUpdateTime;
private $started = false;
private static $formatters;
private static $formats;
/**
* @param string|null $format Indicator format
* @param int $indicatorChangeInterval Change interval in milliseconds
* @param array|null $indicatorValues Animated indicator characters
*/
public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
{
$this->output = $output;
if (null === $format) {
$format = $this->determineBestFormat();
}
if (null === $indicatorValues) {
$indicatorValues = ['-', '\\', '|', '/'];
}
$indicatorValues = array_values($indicatorValues);
if (2 > \count($indicatorValues)) {
throw new InvalidArgumentException('Must have at least 2 indicator value characters.');
}
$this->format = self::getFormatDefinition($format);
$this->indicatorChangeInterval = $indicatorChangeInterval;
$this->indicatorValues = $indicatorValues;
$this->startTime = time();
}
/**
* Sets the current indicator message.
*
* @param string|null $message
*/
public function setMessage($message)
{
$this->message = $message;
$this->display();
}
/**
* Starts the indicator output.
*
* @param $message
*/
public function start($message)
{
if ($this->started) {
throw new LogicException('Progress indicator already started.');
}
$this->message = $message;
$this->started = true;
$this->startTime = time();
$this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;
$this->indicatorCurrent = 0;
$this->display();
}
/**
* Advances the indicator.
*/
public function advance()
{
if (!$this->started) {
throw new LogicException('Progress indicator has not yet been started.');
}
if (!$this->output->isDecorated()) {
return;
}
$currentTime = $this->getCurrentTimeInMilliseconds();
if ($currentTime < $this->indicatorUpdateTime) {
return;
}
$this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;
++$this->indicatorCurrent;
$this->display();
}
/**
* Finish the indicator with message.
*
* @param $message
*/
public function finish($message)
{
if (!$this->started) {
throw new LogicException('Progress indicator has not yet been started.');
}
$this->message = $message;
$this->display();
$this->output->writeln('');
$this->started = false;
}
/**
* Gets the format for a given name.
*
* @param string $name The format name
*
* @return string|null A format string
*/
public static function getFormatDefinition($name)
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
}
/**
* Sets a placeholder formatter for a given name.
*
* This method also allow you to override an existing placeholder.
*
* @param string $name The placeholder name (including the delimiter char like %)
* @param callable $callable A PHP callable
*/
public static function setPlaceholderFormatterDefinition($name, $callable)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
self::$formatters[$name] = $callable;
}
/**
* Gets the placeholder formatter for a given name.
*
* @param string $name The placeholder name (including the delimiter char like %)
*
* @return callable|null A PHP callable
*/
public static function getPlaceholderFormatterDefinition($name)
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
}
private function display()
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}
$self = $this;
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) {
if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
return $formatter($self);
}
return $matches[0];
}, $this->format));
}
private function determineBestFormat()
{
switch ($this->output->getVerbosity()) {
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
case OutputInterface::VERBOSITY_VERBOSE:
return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';
case OutputInterface::VERBOSITY_VERY_VERBOSE:
case OutputInterface::VERBOSITY_DEBUG:
return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi';
default:
return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi';
}
}
/**
* Overwrites a previous message to the output.
*/
private function overwrite(string $message)
{
if ($this->output->isDecorated()) {
$this->output->write("\x0D\x1B[2K");
$this->output->write($message);
} else {
$this->output->writeln($message);
}
}
private function getCurrentTimeInMilliseconds()
{
return round(microtime(true) * 1000);
}
private static function initPlaceholderFormatters()
{
return [
'indicator' => function (self $indicator) {
return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];
},
'message' => function (self $indicator) {
return $indicator->message;
},
'elapsed' => function (self $indicator) {
return Helper::formatTime(time() - $indicator->startTime);
},
'memory' => function () {
return Helper::formatMemory(memory_get_usage(true));
},
];
}
private static function initFormats()
{
return [
'normal' => ' %indicator% %message%',
'normal_no_ansi' => ' %message%',
'verbose' => ' %indicator% %message% (%elapsed:6s%)',
'verbose_no_ansi' => ' %message% (%elapsed:6s%)',
'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
];
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
/**
* @internal
*/
class TableRows implements \IteratorAggregate
{
private $generator;
public function __construct(callable $generator)
{
$this->generator = $generator;
}
public function getIterator()
{
$g = $this->generator;
return $g();
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal;
/**
* The ProgressBar provides helpers to display progress output.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Chris Jones <leeked@gmail.com>
*/
final class ProgressBar
{
private $barWidth = 28;
private $barChar;
private $emptyBarChar = '-';
private $progressChar = '>';
private $format;
private $internalFormat;
private $redrawFreq = 1;
private $output;
private $step = 0;
private $max;
private $startTime;
private $stepWidth;
private $percent = 0.0;
private $formatLineCount;
private $messages = [];
private $overwrite = true;
private $terminal;
private $firstRun = true;
private static $formatters;
private static $formats;
/**
* @param OutputInterface $output An OutputInterface instance
* @param int $max Maximum steps (0 if unknown)
*/
public function __construct(OutputInterface $output, int $max = 0)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$this->output = $output;
$this->setMaxSteps($max);
$this->terminal = new Terminal();
if (!$this->output->isDecorated()) {
// disable overwrite when output does not support ANSI codes.
$this->overwrite = false;
// set a reasonable redraw frequency so output isn't flooded
$this->setRedrawFrequency($max / 10);
}
$this->startTime = time();
}
/**
* Sets a placeholder formatter for a given name.
*
* This method also allow you to override an existing placeholder.
*
* @param string $name The placeholder name (including the delimiter char like %)
* @param callable $callable A PHP callable
*/
public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
self::$formatters[$name] = $callable;
}
/**
* Gets the placeholder formatter for a given name.
*
* @param string $name The placeholder name (including the delimiter char like %)
*
* @return callable|null A PHP callable
*/
public static function getPlaceholderFormatterDefinition(string $name): ?callable
{
if (!self::$formatters) {
self::$formatters = self::initPlaceholderFormatters();
}
return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
}
/**
* Sets a format for a given name.
*
* This method also allow you to override an existing format.
*
* @param string $name The format name
* @param string $format A format string
*/
public static function setFormatDefinition(string $name, string $format): void
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
self::$formats[$name] = $format;
}
/**
* Gets the format for a given name.
*
* @param string $name The format name
*
* @return string|null A format string
*/
public static function getFormatDefinition(string $name): ?string
{
if (!self::$formats) {
self::$formats = self::initFormats();
}
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
}
/**
* Associates a text with a named placeholder.
*
* The text is displayed when the progress bar is rendered but only
* when the corresponding placeholder is part of the custom format line
* (by wrapping the name with %).
*
* @param string $message The text to associate with the placeholder
* @param string $name The name of the placeholder
*/
public function setMessage(string $message, string $name = 'message')
{
$this->messages[$name] = $message;
}
public function getMessage(string $name = 'message')
{
return $this->messages[$name];
}
public function getStartTime(): int
{
return $this->startTime;
}
public function getMaxSteps(): int
{
return $this->max;
}
public function getProgress(): int
{
return $this->step;
}
private function getStepWidth(): int
{
return $this->stepWidth;
}
public function getProgressPercent(): float
{
return $this->percent;
}
public function setBarWidth(int $size)
{
$this->barWidth = max(1, $size);
}
public function getBarWidth(): int
{
return $this->barWidth;
}
public function setBarCharacter(string $char)
{
$this->barChar = $char;
}
public function getBarCharacter(): string
{
if (null === $this->barChar) {
return $this->max ? '=' : $this->emptyBarChar;
}
return $this->barChar;
}
public function setEmptyBarCharacter(string $char)
{
$this->emptyBarChar = $char;
}
public function getEmptyBarCharacter(): string
{
return $this->emptyBarChar;
}
public function setProgressCharacter(string $char)
{
$this->progressChar = $char;
}
public function getProgressCharacter(): string
{
return $this->progressChar;
}
public function setFormat(string $format)
{
$this->format = null;
$this->internalFormat = $format;
}
/**
* Sets the redraw frequency.
*
* @param int|float $freq The frequency in steps
*/
public function setRedrawFrequency(int $freq)
{
$this->redrawFreq = max($freq, 1);
}
/**
* Returns an iterator that will automatically update the progress bar when iterated.
*
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable
*/
public function iterate(iterable $iterable, int $max = null): iterable
{
$this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0));
foreach ($iterable as $key => $value) {
yield $key => $value;
$this->advance();
}
$this->finish();
}
/**
* Starts the progress output.
*
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
*/
public function start(int $max = null)
{
$this->startTime = time();
$this->step = 0;
$this->percent = 0.0;
if (null !== $max) {
$this->setMaxSteps($max);
}
$this->display();
}
/**
* Advances the progress output X steps.
*
* @param int $step Number of steps to advance
*/
public function advance(int $step = 1)
{
$this->setProgress($this->step + $step);
}
/**
* Sets whether to overwrite the progressbar, false for new line.
*/
public function setOverwrite(bool $overwrite)
{
$this->overwrite = $overwrite;
}
public function setProgress(int $step)
{
if ($this->max && $step > $this->max) {
$this->max = $step;
} elseif ($step < 0) {
$step = 0;
}
$prevPeriod = (int) ($this->step / $this->redrawFreq);
$currPeriod = (int) ($step / $this->redrawFreq);
$this->step = $step;
$this->percent = $this->max ? (float) $this->step / $this->max : 0;
if ($prevPeriod !== $currPeriod || $this->max === $step) {
$this->display();
}
}
public function setMaxSteps(int $max)
{
$this->format = null;
$this->max = max(0, $max);
$this->stepWidth = $this->max ? Helper::strlen((string) $this->max) : 4;
}
/**
* Finishes the progress output.
*/
public function finish(): void
{
if (!$this->max) {
$this->max = $this->step;
}
if ($this->step === $this->max && !$this->overwrite) {
// prevent double 100% output
return;
}
$this->setProgress($this->max);
}
/**
* Outputs the current progress string.
*/
public function display(): void
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}
if (null === $this->format) {
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}
$this->overwrite($this->buildLine());
}
/**
* Removes the progress bar from the current line.
*
* This is useful if you wish to write some output
* while a progress bar is running.
* Call display() to show the progress bar again.
*/
public function clear(): void
{
if (!$this->overwrite) {
return;
}
if (null === $this->format) {
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}
$this->overwrite('');
}
private function setRealFormat(string $format)
{
// try to use the _nomax variant if available
if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
$this->format = self::getFormatDefinition($format.'_nomax');
} elseif (null !== self::getFormatDefinition($format)) {
$this->format = self::getFormatDefinition($format);
} else {
$this->format = $format;
}
$this->formatLineCount = substr_count($this->format, "\n");
}
/**
* Overwrites a previous message to the output.
*/
private function overwrite(string $message): void
{
if ($this->overwrite) {
if (!$this->firstRun) {
if ($this->output instanceof ConsoleSectionOutput) {
$lines = floor(Helper::strlen($message) / $this->terminal->getWidth()) + $this->formatLineCount + 1;
$this->output->clear($lines);
} else {
// Erase previous lines
if ($this->formatLineCount > 0) {
$message = str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount).$message;
}
// Move the cursor to the beginning of the line and erase the line
$message = "\x0D\x1B[2K$message";
}
}
} elseif ($this->step > 0) {
$message = PHP_EOL.$message;
}
$this->firstRun = false;
$this->output->write($message);
}
private function determineBestFormat(): string
{
switch ($this->output->getVerbosity()) {
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
case OutputInterface::VERBOSITY_VERBOSE:
return $this->max ? 'verbose' : 'verbose_nomax';
case OutputInterface::VERBOSITY_VERY_VERBOSE:
return $this->max ? 'very_verbose' : 'very_verbose_nomax';
case OutputInterface::VERBOSITY_DEBUG:
return $this->max ? 'debug' : 'debug_nomax';
default:
return $this->max ? 'normal' : 'normal_nomax';
}
}
private static function initPlaceholderFormatters(): array
{
return [
'bar' => function (self $bar, OutputInterface $output) {
$completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth());
$display = str_repeat($bar->getBarCharacter(), $completeBars);
if ($completeBars < $bar->getBarWidth()) {
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
$display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
}
return $display;
},
'elapsed' => function (self $bar) {
return Helper::formatTime(time() - $bar->getStartTime());
},
'remaining' => function (self $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
}
if (!$bar->getProgress()) {
$remaining = 0;
} else {
$remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress()));
}
return Helper::formatTime($remaining);
},
'estimated' => function (self $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
}
if (!$bar->getProgress()) {
$estimated = 0;
} else {
$estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps());
}
return Helper::formatTime($estimated);
},
'memory' => function (self $bar) {
return Helper::formatMemory(memory_get_usage(true));
},
'current' => function (self $bar) {
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
},
'max' => function (self $bar) {
return $bar->getMaxSteps();
},
'percent' => function (self $bar) {
return floor($bar->getProgressPercent() * 100);
},
];
}
private static function initFormats(): array
{
return [
'normal' => ' %current%/%max% [%bar%] %percent:3s%%',
'normal_nomax' => ' %current% [%bar%]',
'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%',
'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',
'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%',
'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',
'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
];
}
private function buildLine(): string
{
$regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
$callback = function ($matches) {
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
$text = $formatter($this, $this->output);
} elseif (isset($this->messages[$matches[1]])) {
$text = $this->messages[$matches[1]];
} else {
return $matches[0];
}
if (isset($matches[2])) {
$text = sprintf('%'.$matches[2], $text);
}
return $text;
};
$line = preg_replace_callback($regex, $callback, $this->format);
// gets string length for each sub line with multiline format
$linesLength = array_map(function ($subLine) {
return Helper::strlenWithoutDecoration($this->output->getFormatter(), rtrim($subLine, "\r"));
}, explode("\n", $line));
$linesWidth = max($linesLength);
$terminalWidth = $this->terminal->getWidth();
if ($linesWidth <= $terminalWidth) {
return $line;
}
$this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth);
return preg_replace_callback($regex, $callback, $this->format);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StreamableInputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\ConsoleSectionOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;
/**
* The QuestionHelper class provides helpers to interact with the user.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class QuestionHelper extends Helper
{
private $inputStream;
private static $shell;
private static $stty;
/**
* Asks a question to the user.
*
* @return mixed The user answer
*
* @throws RuntimeException If there is no data to read in the input stream
*/
public function ask(InputInterface $input, OutputInterface $output, Question $question)
{
if ($output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
if (!$input->isInteractive()) {
$default = $question->getDefault();
if (null === $default) {
return $default;
}
if ($validator = $question->getValidator()) {
return \call_user_func($question->getValidator(), $default);
} elseif ($question instanceof ChoiceQuestion) {
$choices = $question->getChoices();
if (!$question->isMultiselect()) {
return isset($choices[$default]) ? $choices[$default] : $default;
}
$default = explode(',', $default);
foreach ($default as $k => $v) {
$v = trim($v);
$default[$k] = isset($choices[$v]) ? $choices[$v] : $v;
}
}
return $default;
}
if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
$this->inputStream = $stream;
}
if (!$question->getValidator()) {
return $this->doAsk($output, $question);
}
$interviewer = function () use ($output, $question) {
return $this->doAsk($output, $question);
};
return $this->validateAttempts($interviewer, $output, $question);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'question';
}
/**
* Prevents usage of stty.
*/
public static function disableStty()
{
self::$stty = false;
}
/**
* Asks the question to the user.
*
* @return bool|mixed|string|null
*
* @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
*/
private function doAsk(OutputInterface $output, Question $question)
{
$this->writePrompt($output, $question);
$inputStream = $this->inputStream ?: STDIN;
$autocomplete = $question->getAutocompleterCallback();
if (null === $autocomplete || !Terminal::hasSttyAvailable()) {
$ret = false;
if ($question->isHidden()) {
try {
$ret = trim($this->getHiddenResponse($output, $inputStream));
} catch (RuntimeException $e) {
if (!$question->isHiddenFallback()) {
throw $e;
}
}
}
if (false === $ret) {
$ret = fgets($inputStream, 4096);
if (false === $ret) {
throw new RuntimeException('Aborted.');
}
$ret = trim($ret);
}
} else {
$ret = trim($this->autocomplete($output, $question, $inputStream, $autocomplete));
}
if ($output instanceof ConsoleSectionOutput) {
$output->addContent($ret);
}
$ret = \strlen($ret) > 0 ? $ret : $question->getDefault();
if ($normalizer = $question->getNormalizer()) {
return $normalizer($ret);
}
return $ret;
}
/**
* Outputs the question prompt.
*/
protected function writePrompt(OutputInterface $output, Question $question)
{
$message = $question->getQuestion();
if ($question instanceof ChoiceQuestion) {
$maxWidth = max(array_map([$this, 'strlen'], array_keys($question->getChoices())));
$messages = (array) $question->getQuestion();
foreach ($question->getChoices() as $key => $value) {
$width = $maxWidth - $this->strlen($key);
$messages[] = ' [<info>'.$key.str_repeat(' ', $width).'</info>] '.$value;
}
$output->writeln($messages);
$message = $question->getPrompt();
}
$output->write($message);
}
/**
* Outputs an error message.
*/
protected function writeError(OutputInterface $output, \Exception $error)
{
if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
$message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
} else {
$message = '<error>'.$error->getMessage().'</error>';
}
$output->writeln($message);
}
/**
* Autocompletes a question.
*
* @param resource $inputStream
*/
private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
{
$fullChoice = '';
$ret = '';
$i = 0;
$ofs = -1;
$matches = $autocomplete($ret);
$numMatches = \count($matches);
$sttyMode = shell_exec('stty -g');
// Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
shell_exec('stty -icanon -echo');
// Add highlighted text style
$output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
// Read a keypress
while (!feof($inputStream)) {
$c = fread($inputStream, 1);
// as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
shell_exec(sprintf('stty %s', $sttyMode));
throw new RuntimeException('Aborted.');
} elseif ("\177" === $c) { // Backspace Character
if (0 === $numMatches && 0 !== $i) {
--$i;
$fullChoice = substr($fullChoice, 0, -1);
// Move cursor backwards
$output->write("\033[1D");
}
if (0 === $i) {
$ofs = -1;
$matches = $autocomplete($ret);
$numMatches = \count($matches);
} else {
$numMatches = 0;
}
// Pop the last character off the end of our string
$ret = substr($ret, 0, $i);
} elseif ("\033" === $c) {
// Did we read an escape sequence?
$c .= fread($inputStream, 2);
// A = Up Arrow. B = Down Arrow
if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
if ('A' === $c[2] && -1 === $ofs) {
$ofs = 0;
}
if (0 === $numMatches) {
continue;
}
$ofs += ('A' === $c[2]) ? -1 : 1;
$ofs = ($numMatches + $ofs) % $numMatches;
}
} elseif (\ord($c) < 32) {
if ("\t" === $c || "\n" === $c) {
if ($numMatches > 0 && -1 !== $ofs) {
$ret = (string) $matches[$ofs];
// Echo out remaining chars for current match
$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
$output->write($remainingCharacters);
$fullChoice .= $remainingCharacters;
$i = \strlen($fullChoice);
$matches = array_filter(
$autocomplete($ret),
function ($match) use ($ret) {
return '' === $ret || 0 === strpos($match, $ret);
}
);
$numMatches = \count($matches);
$ofs = -1;
}
if ("\n" === $c) {
$output->write($c);
break;
}
}
continue;
} else {
if ("\x80" <= $c) {
$c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
}
$output->write($c);
$ret .= $c;
$fullChoice .= $c;
++$i;
$tempRet = $ret;
if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
$tempRet = $this->mostRecentlyEnteredValue($fullChoice);
}
$numMatches = 0;
$ofs = 0;
foreach ($autocomplete($ret) as $value) {
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
if (0 === strpos($value, $tempRet)) {
$matches[$numMatches++] = $value;
}
}
}
// Erase characters from cursor to end of line
$output->write("\033[K");
if ($numMatches > 0 && -1 !== $ofs) {
// Save cursor position
$output->write("\0337");
// Write highlighted text, complete the partially entered response
$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
// Restore cursor position
$output->write("\0338");
}
}
// Reset stty so it behaves normally again
shell_exec(sprintf('stty %s', $sttyMode));
return $fullChoice;
}
private function mostRecentlyEnteredValue($entered)
{
// Determine the most recent value that the user entered
if (false === strpos($entered, ',')) {
return $entered;
}
$choices = explode(',', $entered);
if (\strlen($lastChoice = trim($choices[\count($choices) - 1])) > 0) {
return $lastChoice;
}
return $entered;
}
/**
* Gets a hidden response from user.
*
* @param OutputInterface $output An Output instance
* @param resource $inputStream The handler resource
*
* @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
*/
private function getHiddenResponse(OutputInterface $output, $inputStream): string
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
// handle code running from a phar
if ('phar:' === substr(__FILE__, 0, 5)) {
$tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
copy($exe, $tmpExe);
$exe = $tmpExe;
}
$value = rtrim(shell_exec($exe));
$output->writeln('');
if (isset($tmpExe)) {
unlink($tmpExe);
}
return $value;
}
if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
shell_exec('stty -echo');
$value = fgets($inputStream, 4096);
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new RuntimeException('Aborted.');
}
$value = trim($value);
$output->writeln('');
return $value;
}
if (false !== $shell = $this->getShell()) {
$readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
$value = rtrim(shell_exec($command));
$output->writeln('');
return $value;
}
throw new RuntimeException('Unable to hide the response.');
}
/**
* Validates an attempt.
*
* @param callable $interviewer A callable that will ask for a question and return the result
* @param OutputInterface $output An Output instance
* @param Question $question A Question instance
*
* @return mixed The validated response
*
* @throws \Exception In case the max number of attempts has been reached and no valid response has been given
*/
private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question)
{
$error = null;
$attempts = $question->getMaxAttempts();
while (null === $attempts || $attempts--) {
if (null !== $error) {
$this->writeError($output, $error);
}
try {
return $question->getValidator()($interviewer());
} catch (RuntimeException $e) {
throw $e;
} catch (\Exception $error) {
}
}
throw $error;
}
/**
* Returns a valid unix shell.
*
* @return string|bool The valid shell name, false in case no valid shell is found
*/
private function getShell()
{
if (null !== self::$shell) {
return self::$shell;
}
self::$shell = false;
if (file_exists('/usr/bin/env')) {
// handle other OSs with bash/zsh/ksh/csh if available to hide the answer
$test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) {
if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
self::$shell = $sh;
break;
}
}
}
return self::$shell;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Symfony Style Guide compliant question helper.
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
class SymfonyQuestionHelper extends QuestionHelper
{
/**
* {@inheritdoc}
*/
protected function writePrompt(OutputInterface $output, Question $question)
{
$text = OutputFormatter::escapeTrailingBackslash($question->getQuestion());
$default = $question->getDefault();
switch (true) {
case null === $default:
$text = sprintf(' <info>%s</info>:', $text);
break;
case $question instanceof ConfirmationQuestion:
$text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no');
break;
case $question instanceof ChoiceQuestion && $question->isMultiselect():
$choices = $question->getChoices();
$default = explode(',', $default);
foreach ($default as $key => $value) {
$default[$key] = $choices[trim($value)];
}
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape(implode(', ', $default)));
break;
case $question instanceof ChoiceQuestion:
$choices = $question->getChoices();
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape(isset($choices[$default]) ? $choices[$default] : $default));
break;
default:
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($default));
}
$output->writeln($text);
if ($question instanceof ChoiceQuestion) {
$width = max(array_map('strlen', array_keys($question->getChoices())));
foreach ($question->getChoices() as $key => $value) {
$output->writeln(sprintf(" [<comment>%-${width}s</comment>] %s", $key, $value));
}
}
$output->write(' > ');
}
/**
* {@inheritdoc}
*/
protected function writeError(OutputInterface $output, \Exception $error)
{
if ($output instanceof SymfonyStyle) {
$output->newLine();
$output->error($error->getMessage());
return;
}
parent::writeError($output, $error);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Logger;
use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* PSR-3 compliant console logger.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*
* @see https://www.php-fig.org/psr/psr-3/
*/
class ConsoleLogger extends AbstractLogger
{
const INFO = 'info';
const ERROR = 'error';
private $output;
private $verbosityLevelMap = [
LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE,
LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE,
LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG,
];
private $formatLevelMap = [
LogLevel::EMERGENCY => self::ERROR,
LogLevel::ALERT => self::ERROR,
LogLevel::CRITICAL => self::ERROR,
LogLevel::ERROR => self::ERROR,
LogLevel::WARNING => self::INFO,
LogLevel::NOTICE => self::INFO,
LogLevel::INFO => self::INFO,
LogLevel::DEBUG => self::INFO,
];
private $errored = false;
public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = [])
{
$this->output = $output;
$this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
$this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
}
/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = [])
{
if (!isset($this->verbosityLevelMap[$level])) {
throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
}
$output = $this->output;
// Write to the error output if necessary and available
if (self::ERROR === $this->formatLevelMap[$level]) {
if ($this->output instanceof ConsoleOutputInterface) {
$output = $output->getErrorOutput();
}
$this->errored = true;
}
// the if condition check isn't necessary -- it's the same one that $output will do internally anyway.
// We only do it for efficiency here as the message formatting is relatively expensive.
if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) {
$output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]);
}
}
/**
* Returns true when any messages have been logged at error levels.
*
* @return bool
*/
public function hasErrored()
{
return $this->errored;
}
/**
* Interpolates context values into the message placeholders.
*
* @author PHP Framework Interoperability Group
*/
private function interpolate(string $message, array $context): string
{
if (false === strpos($message, '{')) {
return $message;
}
$replacements = [];
foreach ($context as $key => $val) {
if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
$replacements["{{$key}}"] = $val;
} elseif ($val instanceof \DateTimeInterface) {
$replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);
} elseif (\is_object($val)) {
$replacements["{{$key}}"] = '[object '.\get_class($val).']';
} else {
$replacements["{{$key}}"] = '['.\gettype($val).']';
}
}
return strtr($message, $replacements);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console;
class Terminal
{
private static $width;
private static $height;
private static $stty;
/**
* Gets the terminal width.
*
* @return int
*/
public function getWidth()
{
$width = getenv('COLUMNS');
if (false !== $width) {
return (int) trim($width);
}
if (null === self::$width) {
self::initDimensions();
}
return self::$width ?: 80;
}
/**
* Gets the terminal height.
*
* @return int
*/
public function getHeight()
{
$height = getenv('LINES');
if (false !== $height) {
return (int) trim($height);
}
if (null === self::$height) {
self::initDimensions();
}
return self::$height ?: 50;
}
/**
* @internal
*
* @return bool
*/
public static function hasSttyAvailable()
{
if (null !== self::$stty) {
return self::$stty;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = 0 === $exitcode;
}
private static function initDimensions()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) {
// extract [w, H] from "wxh (WxH)"
// or [w, h] from "wxh"
self::$width = (int) $matches[1];
self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
} elseif (!self::hasVt100Support() && self::hasSttyAvailable()) {
// only use stty on Windows if the terminal does not support vt100 (e.g. Windows 7 + git-bash)
// testing for stty in a Windows 10 vt100-enabled console will implicitly disable vt100 support on STDOUT
self::initDimensionsUsingStty();
} elseif (null !== $dimensions = self::getConsoleMode()) {
// extract [w, h] from "wxh"
self::$width = (int) $dimensions[0];
self::$height = (int) $dimensions[1];
}
} else {
self::initDimensionsUsingStty();
}
}
/**
* Returns whether STDOUT has vt100 support (some Windows 10+ configurations).
*/
private static function hasVt100Support(): bool
{
return \function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(fopen('php://stdout', 'w'));
}
/**
* Initializes dimensions using the output of an stty columns line.
*/
private static function initDimensionsUsingStty()
{
if ($sttyString = self::getSttyColumns()) {
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
// extract [w, h] from "rows h; columns w;"
self::$width = (int) $matches[2];
self::$height = (int) $matches[1];
} elseif (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
// extract [w, h] from "; h rows; w columns"
self::$width = (int) $matches[2];
self::$height = (int) $matches[1];
}
}
}
/**
* Runs and parses mode CON if it's available, suppressing any error output.
*
* @return int[]|null An array composed of the width and the height or null if it could not be parsed
*/
private static function getConsoleMode()
{
$info = self::readFromProcess('mode CON');
if (null === $info || !preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
return null;
}
return [(int) $matches[2], (int) $matches[1]];
}
/**
* Runs and parses stty -a if it's available, suppressing any error output.
*
* @return string|null
*/
private static function getSttyColumns()
{
return self::readFromProcess('stty -a | grep columns');
}
/**
* @param string $command
*
* @return string|null
*/
private static function readFromProcess($command)
{
if (!\function_exists('proc_open')) {
return null;
}
$descriptorspec = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
$process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
if (!\is_resource($process)) {
return null;
}
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $info;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\CommandLoader;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* Loads commands from a PSR-11 container.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class ContainerCommandLoader implements CommandLoaderInterface
{
private $container;
private $commandMap;
/**
* @param ContainerInterface $container A container from which to load command services
* @param array $commandMap An array with command names as keys and service ids as values
*/
public function __construct(ContainerInterface $container, array $commandMap)
{
$this->container = $container;
$this->commandMap = $commandMap;
}
/**
* {@inheritdoc}
*/
public function get($name)
{
if (!$this->has($name)) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}
return $this->container->get($this->commandMap[$name]);
}
/**
* {@inheritdoc}
*/
public function has($name)
{
return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]);
}
/**
* {@inheritdoc}
*/
public function getNames()
{
return array_keys($this->commandMap);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\CommandLoader;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* A simple command loader using factories to instantiate commands lazily.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
class FactoryCommandLoader implements CommandLoaderInterface
{
private $factories;
/**
* @param callable[] $factories Indexed by command names
*/
public function __construct(array $factories)
{
$this->factories = $factories;
}
/**
* {@inheritdoc}
*/
public function has($name)
{
return isset($this->factories[$name]);
}
/**
* {@inheritdoc}
*/
public function get($name)
{
if (!isset($this->factories[$name])) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}
$factory = $this->factories[$name];
return $factory();
}
/**
* {@inheritdoc}
*/
public function getNames()
{
return array_keys($this->factories);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\CommandLoader;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* @author Robin Chalas <robin.chalas@gmail.com>
*/
interface CommandLoaderInterface
{
/**
* Loads a command.
*
* @param string $name
*
* @return Command
*
* @throws CommandNotFoundException
*/
public function get($name);
/**
* Checks if a command exists.
*
* @param string $name
*
* @return bool
*/
public function has($name);
/**
* @return string[] All registered command names
*/
public function getNames();
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Exception\NamespaceNotFoundException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\DebugFormatterHelper;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StreamableInputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
/**
* An Application is the container for a collection of commands.
*
* It is the main entry point of a Console application.
*
* This class is optimized for a standard CLI environment.
*
* Usage:
*
* $app = new Application('myapp', '1.0 (stable)');
* $app->add(new SimpleCommand());
* $app->run();
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Application
{
private $commands = [];
private $wantHelps = false;
private $runningCommand;
private $name;
private $version;
private $commandLoader;
private $catchExceptions = true;
private $autoExit = true;
private $definition;
private $helperSet;
private $dispatcher;
private $terminal;
private $defaultCommand;
private $singleCommand = false;
private $initialized;
/**
* @param string $name The name of the application
* @param string $version The version of the application
*/
public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
{
$this->name = $name;
$this->version = $version;
$this->terminal = new Terminal();
$this->defaultCommand = 'list';
}
/**
* @final since Symfony 4.3, the type-hint will be updated to the interface from symfony/contracts in 5.0
*/
public function setDispatcher(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
}
public function setCommandLoader(CommandLoaderInterface $commandLoader)
{
$this->commandLoader = $commandLoader;
}
/**
* Runs the current application.
*
* @return int 0 if everything went fine, or an error code
*
* @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}.
*/
public function run(InputInterface $input = null, OutputInterface $output = null)
{
putenv('LINES='.$this->terminal->getHeight());
putenv('COLUMNS='.$this->terminal->getWidth());
if (null === $input) {
$input = new ArgvInput();
}
if (null === $output) {
$output = new ConsoleOutput();
}
$renderException = function ($e) use ($output) {
if (!$e instanceof \Exception) {
$e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
}
if ($output instanceof ConsoleOutputInterface) {
$this->renderException($e, $output->getErrorOutput());
} else {
$this->renderException($e, $output);
}
};
if ($phpHandler = set_exception_handler($renderException)) {
restore_exception_handler();
if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
$debugHandler = true;
} elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
$phpHandler[0]->setExceptionHandler($debugHandler);
}
}
$this->configureIO($input, $output);
try {
$exitCode = $this->doRun($input, $output);
} catch (\Exception $e) {
if (!$this->catchExceptions) {
throw $e;
}
$renderException($e);
$exitCode = $e->getCode();
if (is_numeric($exitCode)) {
$exitCode = (int) $exitCode;
if (0 === $exitCode) {
$exitCode = 1;
}
} else {
$exitCode = 1;
}
} finally {
// if the exception handler changed, keep it
// otherwise, unregister $renderException
if (!$phpHandler) {
if (set_exception_handler($renderException) === $renderException) {
restore_exception_handler();
}
restore_exception_handler();
} elseif (!$debugHandler) {
$finalHandler = $phpHandler[0]->setExceptionHandler(null);
if ($finalHandler !== $renderException) {
$phpHandler[0]->setExceptionHandler($finalHandler);
}
}
}
if ($this->autoExit) {
if ($exitCode > 255) {
$exitCode = 255;
}
exit($exitCode);
}
return $exitCode;
}
/**
* Runs the current application.
*
* @return int 0 if everything went fine, or an error code
*/
public function doRun(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(['--version', '-V'], true)) {
$output->writeln($this->getLongVersion());
return 0;
}
try {
// Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument.
$input->bind($this->getDefinition());
} catch (ExceptionInterface $e) {
// Errors must be ignored, full binding/validation happens later when the command is known.
}
$name = $this->getCommandName($input);
if (true === $input->hasParameterOption(['--help', '-h'], true)) {
if (!$name) {
$name = 'help';
$input = new ArrayInput(['command_name' => $this->defaultCommand]);
} else {
$this->wantHelps = true;
}
}
if (!$name) {
$name = $this->defaultCommand;
$definition = $this->getDefinition();
$definition->setArguments(array_merge(
$definition->getArguments(),
[
'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
]
));
}
try {
$this->runningCommand = null;
// the command name MUST be the first element of the input
$command = $this->find($name);
} catch (\Throwable $e) {
if (!($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) || 1 !== \count($alternatives = $e->getAlternatives()) || !$input->isInteractive()) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
if (0 === $event->getExitCode()) {
return 0;
}
$e = $event->getError();
}
throw $e;
}
$alternative = $alternatives[0];
$style = new SymfonyStyle($input, $output);
$style->block(sprintf("\nCommand \"%s\" is not defined.\n", $name), null, 'error');
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
return $event->getExitCode();
}
return 1;
}
$command = $this->find($alternative);
}
$this->runningCommand = $command;
$exitCode = $this->doRunCommand($command, $input, $output);
$this->runningCommand = null;
return $exitCode;
}
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
}
/**
* Get the helper set associated with the command.
*
* @return HelperSet The HelperSet instance associated with this command
*/
public function getHelperSet()
{
if (!$this->helperSet) {
$this->helperSet = $this->getDefaultHelperSet();
}
return $this->helperSet;
}
public function setDefinition(InputDefinition $definition)
{
$this->definition = $definition;
}
/**
* Gets the InputDefinition related to this Application.
*
* @return InputDefinition The InputDefinition instance
*/
public function getDefinition()
{
if (!$this->definition) {
$this->definition = $this->getDefaultInputDefinition();
}
if ($this->singleCommand) {
$inputDefinition = $this->definition;
$inputDefinition->setArguments();
return $inputDefinition;
}
return $this->definition;
}
/**
* Gets the help message.
*
* @return string A help message
*/
public function getHelp()
{
return $this->getLongVersion();
}
/**
* Gets whether to catch exceptions or not during commands execution.
*
* @return bool Whether to catch exceptions or not during commands execution
*/
public function areExceptionsCaught()
{
return $this->catchExceptions;
}
/**
* Sets whether to catch exceptions or not during commands execution.
*
* @param bool $boolean Whether to catch exceptions or not during commands execution
*/
public function setCatchExceptions($boolean)
{
$this->catchExceptions = (bool) $boolean;
}
/**
* Gets whether to automatically exit after a command execution or not.
*
* @return bool Whether to automatically exit after a command execution or not
*/
public function isAutoExitEnabled()
{
return $this->autoExit;
}
/**
* Sets whether to automatically exit after a command execution or not.
*
* @param bool $boolean Whether to automatically exit after a command execution or not
*/
public function setAutoExit($boolean)
{
$this->autoExit = (bool) $boolean;
}
/**
* Gets the name of the application.
*
* @return string The application name
*/
public function getName()
{
return $this->name;
}
/**
* Sets the application name.
*
* @param string $name The application name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Gets the application version.
*
* @return string The application version
*/
public function getVersion()
{
return $this->version;
}
/**
* Sets the application version.
*
* @param string $version The application version
*/
public function setVersion($version)
{
$this->version = $version;
}
/**
* Returns the long version of the application.
*
* @return string The long application version
*/
public function getLongVersion()
{
if ('UNKNOWN' !== $this->getName()) {
if ('UNKNOWN' !== $this->getVersion()) {
return sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion());
}
return $this->getName();
}
return 'Console Tool';
}
/**
* Registers a new command.
*
* @param string $name The command name
*
* @return Command The newly created command
*/
public function register($name)
{
return $this->add(new Command($name));
}
/**
* Adds an array of command objects.
*
* If a Command is not enabled it will not be added.
*
* @param Command[] $commands An array of commands
*/
public function addCommands(array $commands)
{
foreach ($commands as $command) {
$this->add($command);
}
}
/**
* Adds a command object.
*
* If a command with the same name already exists, it will be overridden.
* If the command is not enabled it will not be added.
*
* @return Command|null The registered command if enabled or null
*/
public function add(Command $command)
{
$this->init();
$command->setApplication($this);
if (!$command->isEnabled()) {
$command->setApplication(null);
return null;
}
// Will throw if the command is not correctly initialized.
$command->getDefinition();
if (!$command->getName()) {
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', \get_class($command)));
}
$this->commands[$command->getName()] = $command;
foreach ($command->getAliases() as $alias) {
$this->commands[$alias] = $command;
}
return $command;
}
/**
* Returns a registered command by name or alias.
*
* @param string $name The command name or alias
*
* @return Command A Command object
*
* @throws CommandNotFoundException When given command name does not exist
*/
public function get($name)
{
$this->init();
if (!$this->has($name)) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}
$command = $this->commands[$name];
if ($this->wantHelps) {
$this->wantHelps = false;
$helpCommand = $this->get('help');
$helpCommand->setCommand($command);
return $helpCommand;
}
return $command;
}
/**
* Returns true if the command exists, false otherwise.
*
* @param string $name The command name or alias
*
* @return bool true if the command exists, false otherwise
*/
public function has($name)
{
$this->init();
return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name)));
}
/**
* Returns an array of all unique namespaces used by currently registered commands.
*
* It does not return the global namespace which always exists.
*
* @return string[] An array of namespaces
*/
public function getNamespaces()
{
$namespaces = [];
foreach ($this->all() as $command) {
if ($command->isHidden()) {
continue;
}
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
foreach ($command->getAliases() as $alias) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
}
}
return array_values(array_unique(array_filter($namespaces)));
}
/**
* Finds a registered namespace by a name or an abbreviation.
*
* @param string $namespace A namespace or abbreviation to search for
*
* @return string A registered namespace
*
* @throws NamespaceNotFoundException When namespace is incorrect or ambiguous
*/
public function findNamespace($namespace)
{
$allNamespaces = $this->getNamespaces();
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace);
$namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
if (empty($namespaces)) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
if (1 == \count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
$message .= "\n\nDid you mean one of these?\n ";
}
$message .= implode("\n ", $alternatives);
}
throw new NamespaceNotFoundException($message, $alternatives);
}
$exact = \in_array($namespace, $namespaces, true);
if (\count($namespaces) > 1 && !$exact) {
throw new NamespaceNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
}
return $exact ? $namespace : reset($namespaces);
}
/**
* Finds a command by name or alias.
*
* Contrary to get, this command tries to find the best
* match if you give it an abbreviation of a name or alias.
*
* @param string $name A command name or a command alias
*
* @return Command A Command instance
*
* @throws CommandNotFoundException When command name is incorrect or ambiguous
*/
public function find($name)
{
$this->init();
$aliases = [];
foreach ($this->commands as $command) {
foreach ($command->getAliases() as $alias) {
if (!$this->has($alias)) {
$this->commands[$alias] = $command;
}
}
}
$allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
$commands = preg_grep('{^'.$expr.'}', $allCommands);
if (empty($commands)) {
$commands = preg_grep('{^'.$expr.'}i', $allCommands);
}
// if no commands matched or we just matched namespaces
if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) {
if (false !== $pos = strrpos($name, ':')) {
// check if a namespace exists and contains commands
$this->findNamespace(substr($name, 0, $pos));
}
$message = sprintf('Command "%s" is not defined.', $name);
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
// remove hidden commands
$alternatives = array_filter($alternatives, function ($name) {
return !$this->get($name)->isHidden();
});
if (1 == \count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
$message .= "\n\nDid you mean one of these?\n ";
}
$message .= implode("\n ", $alternatives);
}
throw new CommandNotFoundException($message, array_values($alternatives));
}
// filter out aliases for commands which are already on the list
if (\count($commands) > 1) {
$commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;
$commands = array_unique(array_filter($commands, function ($nameOrAlias) use ($commandList, $commands, &$aliases) {
$commandName = $commandList[$nameOrAlias] instanceof Command ? $commandList[$nameOrAlias]->getName() : $nameOrAlias;
$aliases[$nameOrAlias] = $commandName;
return $commandName === $nameOrAlias || !\in_array($commandName, $commands);
}));
}
$exact = \in_array($name, $commands, true) || isset($aliases[$name]);
if (\count($commands) > 1 && !$exact) {
$usableWidth = $this->terminal->getWidth() - 10;
$abbrevs = array_values($commands);
$maxLen = 0;
foreach ($abbrevs as $abbrev) {
$maxLen = max(Helper::strlen($abbrev), $maxLen);
}
$abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen) {
if (!$commandList[$cmd] instanceof Command) {
$commandList[$cmd] = $this->commandLoader->get($cmd);
}
if ($commandList[$cmd]->isHidden()) {
return false;
}
$abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();
return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
}, array_values($commands));
$suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs));
throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $name, $suggestions), array_values($commands));
}
return $this->get($exact ? $name : reset($commands));
}
/**
* Gets the commands (registered in the given namespace if provided).
*
* The array keys are the full names and the values the command instances.
*
* @param string $namespace A namespace name
*
* @return Command[] An array of Command instances
*/
public function all($namespace = null)
{
$this->init();
if (null === $namespace) {
if (!$this->commandLoader) {
return $this->commands;
}
$commands = $this->commands;
foreach ($this->commandLoader->getNames() as $name) {
if (!isset($commands[$name]) && $this->has($name)) {
$commands[$name] = $this->get($name);
}
}
return $commands;
}
$commands = [];
foreach ($this->commands as $name => $command) {
if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
$commands[$name] = $command;
}
}
if ($this->commandLoader) {
foreach ($this->commandLoader->getNames() as $name) {
if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) {
$commands[$name] = $this->get($name);
}
}
}
return $commands;
}
/**
* Returns an array of possible abbreviations given a set of names.
*
* @param array $names An array of names
*
* @return array An array of abbreviations
*/
public static function getAbbreviations($names)
{
$abbrevs = [];
foreach ($names as $name) {
for ($len = \strlen($name); $len > 0; --$len) {
$abbrev = substr($name, 0, $len);
$abbrevs[$abbrev][] = $name;
}
}
return $abbrevs;
}
/**
* Renders a caught exception.
*/
public function renderException(\Exception $e, OutputInterface $output)
{
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
$this->doRenderException($e, $output);
if (null !== $this->runningCommand) {
$output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
}
protected function doRenderException(\Exception $e, OutputInterface $output)
{
do {
$message = trim($e->getMessage());
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$class = \get_class($e);
$class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
$title = sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
$len = Helper::strlen($title);
} else {
$len = 0;
}
if (false !== strpos($message, "class@anonymous\0")) {
$message = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
}, $message);
}
$width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX;
$lines = [];
foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) {
foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
// pre-format lines to get the right string length
$lineLength = Helper::strlen($line) + 4;
$lines[] = [$line, $lineLength];
$len = max($lineLength, $len);
}
}
$messages = [];
if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
}
$messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::strlen($title))));
}
foreach ($lines as $line) {
$messages[] = sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
}
$messages[] = $emptyLine;
$messages[] = '';
$output->writeln($messages, OutputInterface::VERBOSITY_QUIET);
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);
// exception related properties
$trace = $e->getTrace();
array_unshift($trace, [
'function' => '',
'file' => $e->getFile() ?: 'n/a',
'line' => $e->getLine() ?: 'n/a',
'args' => [],
]);
for ($i = 0, $count = \count($trace); $i < $count; ++$i) {
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
$function = isset($trace[$i]['function']) ? $trace[$i]['function'] : '';
$file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
$output->writeln(sprintf(' %s%s at <info>%s:%s</info>', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET);
}
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
} while ($e = $e->getPrevious());
}
/**
* Configures the input and output instances based on the user arguments and options.
*/
protected function configureIO(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(['--ansi'], true)) {
$output->setDecorated(true);
} elseif (true === $input->hasParameterOption(['--no-ansi'], true)) {
$output->setDecorated(false);
}
if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) {
$input->setInteractive(false);
} elseif (\function_exists('posix_isatty')) {
$inputStream = null;
if ($input instanceof StreamableInputInterface) {
$inputStream = $input->getStream();
}
if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
$input->setInteractive(false);
}
}
switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break;
case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break;
case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break;
case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break;
default: $shellVerbosity = 0; break;
}
if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
$shellVerbosity = -1;
} else {
if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
$shellVerbosity = 3;
} elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
$shellVerbosity = 2;
} elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
$shellVerbosity = 1;
}
}
if (-1 === $shellVerbosity) {
$input->setInteractive(false);
}
putenv('SHELL_VERBOSITY='.$shellVerbosity);
$_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
$_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
}
/**
* Runs the current command.
*
* If an event dispatcher has been attached to the application,
* events are also dispatched during the life-cycle of the command.
*
* @return int 0 if everything went fine, or an error code
*/
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
foreach ($command->getHelperSet() as $helper) {
if ($helper instanceof InputAwareInterface) {
$helper->setInput($input);
}
}
if (null === $this->dispatcher) {
return $command->run($input, $output);
}
// bind before the console.command event, so the listeners have access to input options/arguments
try {
$command->mergeApplicationDefinition();
$input->bind($command->getDefinition());
} catch (ExceptionInterface $e) {
// ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
}
$event = new ConsoleCommandEvent($command, $input, $output);
$e = null;
try {
$this->dispatcher->dispatch($event, ConsoleEvents::COMMAND);
if ($event->commandShouldRun()) {
$exitCode = $command->run($input, $output);
} else {
$exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
}
} catch (\Throwable $e) {
$event = new ConsoleErrorEvent($input, $output, $e, $command);
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
$e = $event->getError();
if (0 === $exitCode = $event->getExitCode()) {
$e = null;
}
}
$event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
$this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);
if (null !== $e) {
throw $e;
}
return $event->getExitCode();
}
/**
* Gets the name of the command based on input.
*
* @return string|null
*/
protected function getCommandName(InputInterface $input)
{
return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument();
}
/**
* Gets the default input definition.
*
* @return InputDefinition An InputDefinition instance
*/
protected function getDefaultInputDefinition()
{
return new InputDefinition([
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
]);
}
/**
* Gets the default commands that should always be available.
*
* @return Command[] An array of default Command instances
*/
protected function getDefaultCommands()
{
return [new HelpCommand(), new ListCommand()];
}
/**
* Gets the default helper set with the helpers that should always be available.
*
* @return HelperSet A HelperSet instance
*/
protected function getDefaultHelperSet()
{
return new HelperSet([
new FormatterHelper(),
new DebugFormatterHelper(),
new ProcessHelper(),
new QuestionHelper(),
]);
}
/**
* Returns abbreviated suggestions in string format.
*
* @param array $abbrevs Abbreviated suggestions to convert
*
* @return string A formatted string of abbreviated suggestions
*/
private function getAbbreviationSuggestions($abbrevs)
{
return ' '.implode("\n ", $abbrevs);
}
/**
* Returns the namespace part of the command name.
*
* This method is not part of public API and should not be used directly.
*
* @param string $name The full name of the command
* @param string $limit The maximum number of parts of the namespace
*
* @return string The namespace of the command
*/
public function extractNamespace($name, $limit = null)
{
$parts = explode(':', $name, -1);
return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit));
}
/**
* Finds alternative of $name among $collection,
* if nothing is found in $collection, try in $abbrevs.
*
* @param string $name The string
* @param iterable $collection The collection
*
* @return string[] A sorted array of similar string
*/
private function findAlternatives($name, $collection)
{
$threshold = 1e3;
$alternatives = [];
$collectionParts = [];
foreach ($collection as $item) {
$collectionParts[$item] = explode(':', $item);
}
foreach (explode(':', $name) as $i => $subname) {
foreach ($collectionParts as $collectionName => $parts) {
$exists = isset($alternatives[$collectionName]);
if (!isset($parts[$i]) && $exists) {
$alternatives[$collectionName] += $threshold;
continue;
} elseif (!isset($parts[$i])) {
continue;
}
$lev = levenshtein($subname, $parts[$i]);
if ($lev <= \strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
} elseif ($exists) {
$alternatives[$collectionName] += $threshold;
}
}
}
foreach ($collection as $item) {
$lev = levenshtein($name, $item);
if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) {
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
}
}
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE);
return array_keys($alternatives);
}
/**
* Sets the default Command name.
*
* @param string $commandName The Command name
* @param bool $isSingleCommand Set to true if there is only one command in this application
*
* @return self
*/
public function setDefaultCommand($commandName, $isSingleCommand = false)
{
$this->defaultCommand = $commandName;
if ($isSingleCommand) {
// Ensure the command exist
$this->find($commandName);
$this->singleCommand = true;
}
return $this;
}
/**
* @internal
*/
public function isSingleCommand()
{
return $this->singleCommand;
}
private function splitStringByWidth($string, $width)
{
// str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
// additionally, array_slice() is not enough as some character has doubled width.
// we need a function to split string not by character count but by string width
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return str_split($string, $width);
}
$utf8String = mb_convert_encoding($string, 'utf8', $encoding);
$lines = [];
$line = '';
foreach (preg_split('//u', $utf8String) as $char) {
// test if $char could be appended to current line
if (mb_strwidth($line.$char, 'utf8') <= $width) {
$line .= $char;
continue;
}
// if not, push current line to array and make new line
$lines[] = str_pad($line, $width);
$line = $char;
}
$lines[] = \count($lines) ? str_pad($line, $width) : $line;
mb_convert_variables($encoding, 'utf8', $lines);
return $lines;
}
/**
* Returns all namespaces of the command name.
*
* @param string $name The full name of the command
*
* @return string[] The namespaces of the command
*/
private function extractAllNamespaces($name)
{
// -1 as third argument is needed to skip the command short name when exploding
$parts = explode(':', $name, -1);
$namespaces = [];
foreach ($parts as $part) {
if (\count($namespaces)) {
$namespaces[] = end($namespaces).':'.$part;
} else {
$namespaces[] = $part;
}
}
return $namespaces;
}
private function init()
{
if ($this->initialized) {
return;
}
$this->initialized = true;
foreach ($this->getDefaultCommands() as $command) {
$this->add($command);
}
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Style;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Decorates output to add console style guide helpers.
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
abstract class OutputStyle implements OutputInterface, StyleInterface
{
private $output;
public function __construct(OutputInterface $output)
{
$this->output = $output;
}
/**
* {@inheritdoc}
*/
public function newLine($count = 1)
{
$this->output->write(str_repeat(PHP_EOL, $count));
}
/**
* @param int $max
*
* @return ProgressBar
*/
public function createProgressBar($max = 0)
{
return new ProgressBar($this->output, $max);
}
/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
{
$this->output->write($messages, $newline, $type);
}
/**
* {@inheritdoc}
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL)
{
$this->output->writeln($messages, $type);
}
/**
* {@inheritdoc}
*/
public function setVerbosity($level)
{
$this->output->setVerbosity($level);
}
/**
* {@inheritdoc}
*/
public function getVerbosity()
{
return $this->output->getVerbosity();
}
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
$this->output->setDecorated($decorated);
}
/**
* {@inheritdoc}
*/
public function isDecorated()
{
return $this->output->isDecorated();
}
/**
* {@inheritdoc}
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
$this->output->setFormatter($formatter);
}
/**
* {@inheritdoc}
*/
public function getFormatter()
{
return $this->output->getFormatter();
}
/**
* {@inheritdoc}
*/
public function isQuiet()
{
return $this->output->isQuiet();
}
/**
* {@inheritdoc}
*/
public function isVerbose()
{
return $this->output->isVerbose();
}
/**
* {@inheritdoc}
*/
public function isVeryVerbose()
{
return $this->output->isVeryVerbose();
}
/**
* {@inheritdoc}
*/
public function isDebug()
{
return $this->output->isDebug();
}
protected function getErrorOutput()
{
if (!$this->output instanceof ConsoleOutputInterface) {
return $this->output;
}
return $this->output->getErrorOutput();
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Style;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;
/**
* Output decorator helpers for the Symfony Style Guide.
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
class SymfonyStyle extends OutputStyle
{
const MAX_LINE_LENGTH = 120;
private $input;
private $questionHelper;
private $progressBar;
private $lineLength;
private $bufferedOutput;
public function __construct(InputInterface $input, OutputInterface $output)
{
$this->input = $input;
$this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter());
// Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not.
$width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH;
$this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);
parent::__construct($output);
}
/**
* Formats a message as a block of text.
*
* @param string|array $messages The message to write in the block
* @param string|null $type The block type (added in [] on first line)
* @param string|null $style The style to apply to the whole block
* @param string $prefix The prefix for the block
* @param bool $padding Whether to add vertical padding
* @param bool $escape Whether to escape the message
*/
public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = true)
{
$messages = \is_array($messages) ? array_values($messages) : [$messages];
$this->autoPrependBlock();
$this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape));
$this->newLine();
}
/**
* {@inheritdoc}
*/
public function title($message)
{
$this->autoPrependBlock();
$this->writeln([
sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
sprintf('<comment>%s</>', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))),
]);
$this->newLine();
}
/**
* {@inheritdoc}
*/
public function section($message)
{
$this->autoPrependBlock();
$this->writeln([
sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
sprintf('<comment>%s</>', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))),
]);
$this->newLine();
}
/**
* {@inheritdoc}
*/
public function listing(array $elements)
{
$this->autoPrependText();
$elements = array_map(function ($element) {
return sprintf(' * %s', $element);
}, $elements);
$this->writeln($elements);
$this->newLine();
}
/**
* {@inheritdoc}
*/
public function text($message)
{
$this->autoPrependText();
$messages = \is_array($message) ? array_values($message) : [$message];
foreach ($messages as $message) {
$this->writeln(sprintf(' %s', $message));
}
}
/**
* Formats a command comment.
*
* @param string|array $message
*/
public function comment($message)
{
$this->block($message, null, null, '<fg=default;bg=default> // </>', false, false);
}
/**
* {@inheritdoc}
*/
public function success($message)
{
$this->block($message, 'OK', 'fg=black;bg=green', ' ', true);
}
/**
* {@inheritdoc}
*/
public function error($message)
{
$this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true);
}
/**
* {@inheritdoc}
*/
public function warning($message)
{
$this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true);
}
/**
* {@inheritdoc}
*/
public function note($message)
{
$this->block($message, 'NOTE', 'fg=yellow', ' ! ');
}
/**
* {@inheritdoc}
*/
public function caution($message)
{
$this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true);
}
/**
* {@inheritdoc}
*/
public function table(array $headers, array $rows)
{
$style = clone Table::getStyleDefinition('symfony-style-guide');
$style->setCellHeaderFormat('<info>%s</info>');
$table = new Table($this);
$table->setHeaders($headers);
$table->setRows($rows);
$table->setStyle($style);
$table->render();
$this->newLine();
}
/**
* {@inheritdoc}
*/
public function ask($question, $default = null, $validator = null)
{
$question = new Question($question, $default);
$question->setValidator($validator);
return $this->askQuestion($question);
}
/**
* {@inheritdoc}
*/
public function askHidden($question, $validator = null)
{
$question = new Question($question);
$question->setHidden(true);
$question->setValidator($validator);
return $this->askQuestion($question);
}
/**
* {@inheritdoc}
*/
public function confirm($question, $default = true)
{
return $this->askQuestion(new ConfirmationQuestion($question, $default));
}
/**
* {@inheritdoc}
*/
public function choice($question, array $choices, $default = null)
{
if (null !== $default) {
$values = array_flip($choices);
$default = $values[$default];
}
return $this->askQuestion(new ChoiceQuestion($question, $choices, $default));
}
/**
* {@inheritdoc}
*/
public function progressStart($max = 0)
{
$this->progressBar = $this->createProgressBar($max);
$this->progressBar->start();
}
/**
* {@inheritdoc}
*/
public function progressAdvance($step = 1)
{
$this->getProgressBar()->advance($step);
}
/**
* {@inheritdoc}
*/
public function progressFinish()
{
$this->getProgressBar()->finish();
$this->newLine(2);
$this->progressBar = null;
}
/**
* {@inheritdoc}
*/
public function createProgressBar($max = 0)
{
$progressBar = parent::createProgressBar($max);
if ('\\' !== \DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) {
$progressBar->setEmptyBarCharacter('░'); // light shade character \u2591
$progressBar->setProgressCharacter('');
$progressBar->setBarCharacter('▓'); // dark shade character \u2593
}
return $progressBar;
}
/**
* @return mixed
*/
public function askQuestion(Question $question)
{
if ($this->input->isInteractive()) {
$this->autoPrependBlock();
}
if (!$this->questionHelper) {
$this->questionHelper = new SymfonyQuestionHelper();
}
$answer = $this->questionHelper->ask($this->input, $this, $question);
if ($this->input->isInteractive()) {
$this->newLine();
$this->bufferedOutput->write("\n");
}
return $answer;
}
/**
* {@inheritdoc}
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}
foreach ($messages as $message) {
parent::writeln($message, $type);
$this->writeBuffer($message, true, $type);
}
}
/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
$messages = [$messages];
}
foreach ($messages as $message) {
parent::write($message, $newline, $type);
$this->writeBuffer($message, $newline, $type);
}
}
/**
* {@inheritdoc}
*/
public function newLine($count = 1)
{
parent::newLine($count);
$this->bufferedOutput->write(str_repeat("\n", $count));
}
/**
* Returns a new instance which makes use of stderr if available.
*
* @return self
*/
public function getErrorStyle()
{
return new self($this->input, $this->getErrorOutput());
}
private function getProgressBar(): ProgressBar
{
if (!$this->progressBar) {
throw new RuntimeException('The ProgressBar is not started.');
}
return $this->progressBar;
}
private function autoPrependBlock(): void
{
$chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);
if (!isset($chars[0])) {
$this->newLine(); //empty history, so we should start with a new line.
return;
}
//Prepend new line for each non LF chars (This means no blank line was output before)
$this->newLine(2 - substr_count($chars, "\n"));
}
private function autoPrependText(): void
{
$fetched = $this->bufferedOutput->fetch();
//Prepend new line if last char isn't EOL:
if ("\n" !== substr($fetched, -1)) {
$this->newLine();
}
}
private function writeBuffer(string $message, bool $newLine, int $type): void
{
// We need to know if the two last chars are PHP_EOL
// Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer
$this->bufferedOutput->write(substr($message, -4), $newLine, $type);
}
private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false)
{
$indentLength = 0;
$prefixLength = Helper::strlenWithoutDecoration($this->getFormatter(), $prefix);
$lines = [];
if (null !== $type) {
$type = sprintf('[%s] ', $type);
$indentLength = \strlen($type);
$lineIndentation = str_repeat(' ', $indentLength);
}
// wrap and add newlines for each element
foreach ($messages as $key => $message) {
if ($escape) {
$message = OutputFormatter::escape($message);
}
$lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - $prefixLength - $indentLength, PHP_EOL, true)));
if (\count($messages) > 1 && $key < \count($messages) - 1) {
$lines[] = '';
}
}
$firstLineIndex = 0;
if ($padding && $this->isDecorated()) {
$firstLineIndex = 1;
array_unshift($lines, '');
$lines[] = '';
}
foreach ($lines as $i => &$line) {
if (null !== $type) {
$line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line;
}
$line = $prefix.$line;
$line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line));
if ($style) {
$line = sprintf('<%s>%s</>', $style, $line);
}
}
return $lines;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Style;
/**
* Output style helpers.
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
interface StyleInterface
{
/**
* Formats a command title.
*
* @param string $message
*/
public function title($message);
/**
* Formats a section title.
*
* @param string $message
*/
public function section($message);
/**
* Formats a list.
*/
public function listing(array $elements);
/**
* Formats informational text.
*
* @param string|array $message
*/
public function text($message);
/**
* Formats a success result bar.
*
* @param string|array $message
*/
public function success($message);
/**
* Formats an error result bar.
*
* @param string|array $message
*/
public function error($message);
/**
* Formats an warning result bar.
*
* @param string|array $message
*/
public function warning($message);
/**
* Formats a note admonition.
*
* @param string|array $message
*/
public function note($message);
/**
* Formats a caution admonition.
*
* @param string|array $message
*/
public function caution($message);
/**
* Formats a table.
*/
public function table(array $headers, array $rows);
/**
* Asks a question.
*
* @param string $question
* @param string|null $default
* @param callable|null $validator
*
* @return mixed
*/
public function ask($question, $default = null, $validator = null);
/**
* Asks a question with the user input hidden.
*
* @param string $question
* @param callable|null $validator
*
* @return mixed
*/
public function askHidden($question, $validator = null);
/**
* Asks for confirmation.
*
* @param string $question
* @param bool $default
*
* @return bool
*/
public function confirm($question, $default = true);
/**
* Asks a choice question.
*
* @param string $question
* @param string|int|null $default
*
* @return mixed
*/
public function choice($question, array $choices, $default = null);
/**
* Add newline(s).
*
* @param int $count The number of newlines
*/
public function newLine($count = 1);
/**
* Starts the progress output.
*
* @param int $max Maximum steps (0 if unknown)
*/
public function progressStart($max = 0);
/**
* Advances the progress output X steps.
*
* @param int $step Number of steps to advance
*/
public function progressAdvance($step = 1);
/**
* Finishes the progress output.
*/
public function progressFinish();
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\EventListener;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* @author James Halsall <james.t.halsall@googlemail.com>
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class ErrorListener implements EventSubscriberInterface
{
private $logger;
public function __construct(LoggerInterface $logger = null)
{
$this->logger = $logger;
}
public function onConsoleError(ConsoleErrorEvent $event)
{
if (null === $this->logger) {
return;
}
$error = $event->getError();
if (!$inputString = $this->getInputString($event)) {
$this->logger->error('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]);
return;
}
$this->logger->error('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()]);
}
public function onConsoleTerminate(ConsoleTerminateEvent $event)
{
if (null === $this->logger) {
return;
}
$exitCode = $event->getExitCode();
if (0 === $exitCode) {
return;
}
if (!$inputString = $this->getInputString($event)) {
$this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]);
return;
}
$this->logger->debug('Command "{command}" exited with code "{code}"', ['command' => $inputString, 'code' => $exitCode]);
}
public static function getSubscribedEvents()
{
return [
ConsoleEvents::ERROR => ['onConsoleError', -128],
ConsoleEvents::TERMINATE => ['onConsoleTerminate', -128],
];
}
private static function getInputString(ConsoleEvent $event)
{
$commandName = $event->getCommand() ? $event->getCommand()->getName() : null;
$input = $event->getInput();
if (method_exists($input, '__toString')) {
if ($commandName) {
return str_replace(["'$commandName'", "\"$commandName\""], $commandName, (string) $input);
}
return (string) $input;
}
return $commandName;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
/**
* Text descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @internal
*/
class TextDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
} else {
$default = '';
}
$totalWidth = isset($options['total_width']) ? $options['total_width'] : Helper::strlen($argument->getName());
$spacingWidth = $totalWidth - \strlen($argument->getName());
$this->writeText(sprintf(' <info>%s</info> %s%s%s',
$argument->getName(),
str_repeat(' ', $spacingWidth),
// + 4 = 2 spaces before <info>, 2 spaces after </info>
preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()),
$default
), $options);
}
/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = [])
{
if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
} else {
$default = '';
}
$value = '';
if ($option->acceptValue()) {
$value = '='.strtoupper($option->getName());
if ($option->isValueOptional()) {
$value = '['.$value.']';
}
}
$totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions([$option]);
$synopsis = sprintf('%s%s',
$option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ',
sprintf('--%s%s', $option->getName(), $value)
);
$spacingWidth = $totalWidth - Helper::strlen($synopsis);
$this->writeText(sprintf(' <info>%s</info> %s%s%s%s',
$synopsis,
str_repeat(' ', $spacingWidth),
// + 4 = 2 spaces before <info>, 2 spaces after </info>
preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()),
$default,
$option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
), $options);
}
/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
foreach ($definition->getArguments() as $argument) {
$totalWidth = max($totalWidth, Helper::strlen($argument->getName()));
}
if ($definition->getArguments()) {
$this->writeText('<comment>Arguments:</comment>', $options);
$this->writeText("\n");
foreach ($definition->getArguments() as $argument) {
$this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth]));
$this->writeText("\n");
}
}
if ($definition->getArguments() && $definition->getOptions()) {
$this->writeText("\n");
}
if ($definition->getOptions()) {
$laterOptions = [];
$this->writeText('<comment>Options:</comment>', $options);
foreach ($definition->getOptions() as $option) {
if (\strlen($option->getShortcut()) > 1) {
$laterOptions[] = $option;
continue;
}
$this->writeText("\n");
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
}
foreach ($laterOptions as $option) {
$this->writeText("\n");
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
}
}
}
/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = [])
{
$command->getSynopsis(true);
$command->getSynopsis(false);
$command->mergeApplicationDefinition(false);
if ($description = $command->getDescription()) {
$this->writeText('<comment>Description:</comment>', $options);
$this->writeText("\n");
$this->writeText(' '.$description);
$this->writeText("\n\n");
}
$this->writeText('<comment>Usage:</comment>', $options);
foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) {
$this->writeText("\n");
$this->writeText(' '.OutputFormatter::escape($usage), $options);
}
$this->writeText("\n");
$definition = $command->getNativeDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->writeText("\n");
$this->describeInputDefinition($definition, $options);
$this->writeText("\n");
}
$help = $command->getProcessedHelp();
if ($help && $help !== $description) {
$this->writeText("\n");
$this->writeText('<comment>Help:</comment>', $options);
$this->writeText("\n");
$this->writeText(' '.str_replace("\n", "\n ", $help), $options);
$this->writeText("\n");
}
}
/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$description = new ApplicationDescription($application, $describedNamespace);
if (isset($options['raw_text']) && $options['raw_text']) {
$width = $this->getColumnWidth($description->getCommands());
foreach ($description->getCommands() as $command) {
$this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
$this->writeText("\n");
}
} else {
if ('' != $help = $application->getHelp()) {
$this->writeText("$help\n\n", $options);
}
$this->writeText("<comment>Usage:</comment>\n", $options);
$this->writeText(" command [options] [arguments]\n\n", $options);
$this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options);
$this->writeText("\n");
$this->writeText("\n");
$commands = $description->getCommands();
$namespaces = $description->getNamespaces();
if ($describedNamespace && $namespaces) {
// make sure all alias commands are included when describing a specific namespace
$describedNamespaceInfo = reset($namespaces);
foreach ($describedNamespaceInfo['commands'] as $name) {
$commands[$name] = $description->getCommand($name);
}
}
// calculate max. width based on available commands per namespace
$width = $this->getColumnWidth(array_merge(...array_values(array_map(function ($namespace) use ($commands) {
return array_intersect($namespace['commands'], array_keys($commands));
}, $namespaces))));
if ($describedNamespace) {
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
} else {
$this->writeText('<comment>Available commands:</comment>', $options);
}
foreach ($namespaces as $namespace) {
$namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) {
return isset($commands[$name]);
});
if (!$namespace['commands']) {
continue;
}
if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->writeText("\n");
$this->writeText(' <comment>'.$namespace['id'].'</comment>', $options);
}
foreach ($namespace['commands'] as $name) {
$this->writeText("\n");
$spacingWidth = $width - Helper::strlen($name);
$command = $commands[$name];
$commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : '';
$this->writeText(sprintf(' <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options);
}
}
$this->writeText("\n");
}
}
/**
* {@inheritdoc}
*/
private function writeText($content, array $options = [])
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
isset($options['raw_output']) ? !$options['raw_output'] : true
);
}
/**
* Formats command aliases to show them in the command description.
*/
private function getCommandAliasesText(Command $command): string
{
$text = '';
$aliases = $command->getAliases();
if ($aliases) {
$text = '['.implode('|', $aliases).'] ';
}
return $text;
}
/**
* Formats input option/argument default value.
*
* @param mixed $default
*/
private function formatDefaultValue($default): string
{
if (INF === $default) {
return 'INF';
}
if (\is_string($default)) {
$default = OutputFormatter::escape($default);
} elseif (\is_array($default)) {
foreach ($default as $key => $value) {
if (\is_string($value)) {
$default[$key] = OutputFormatter::escape($value);
}
}
}
return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
}
/**
* @param (Command|string)[] $commands
*/
private function getColumnWidth(array $commands): int
{
$widths = [];
foreach ($commands as $command) {
if ($command instanceof Command) {
$widths[] = Helper::strlen($command->getName());
foreach ($command->getAliases() as $alias) {
$widths[] = Helper::strlen($alias);
}
} else {
$widths[] = Helper::strlen($command);
}
}
return $widths ? max($widths) + 2 : 0;
}
/**
* @param InputOption[] $options
*/
private function calculateTotalWidthForOptions(array $options): int
{
$totalWidth = 0;
foreach ($options as $option) {
// "-" + shortcut + ", --" + name
$nameLength = 1 + max(Helper::strlen($option->getShortcut()), 1) + 4 + Helper::strlen($option->getName());
if ($option->acceptValue()) {
$valueLength = 1 + Helper::strlen($option->getName()); // = + value
$valueLength += $option->isValueOptional() ? 2 : 0; // [ + ]
$nameLength += $valueLength;
}
$totalWidth = max($totalWidth, $nameLength);
}
return $totalWidth;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*
* @internal
*/
class ApplicationDescription
{
const GLOBAL_NAMESPACE = '_global';
private $application;
private $namespace;
private $showHidden;
/**
* @var array
*/
private $namespaces;
/**
* @var Command[]
*/
private $commands;
/**
* @var Command[]
*/
private $aliases;
public function __construct(Application $application, string $namespace = null, bool $showHidden = false)
{
$this->application = $application;
$this->namespace = $namespace;
$this->showHidden = $showHidden;
}
/**
* @return array
*/
public function getNamespaces()
{
if (null === $this->namespaces) {
$this->inspectApplication();
}
return $this->namespaces;
}
/**
* @return Command[]
*/
public function getCommands()
{
if (null === $this->commands) {
$this->inspectApplication();
}
return $this->commands;
}
/**
* @param string $name
*
* @return Command
*
* @throws CommandNotFoundException
*/
public function getCommand($name)
{
if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name));
}
return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name];
}
private function inspectApplication()
{
$this->commands = [];
$this->namespaces = [];
$all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null);
foreach ($this->sortCommands($all) as $namespace => $commands) {
$names = [];
/** @var Command $command */
foreach ($commands as $name => $command) {
if (!$command->getName() || (!$this->showHidden && $command->isHidden())) {
continue;
}
if ($command->getName() === $name) {
$this->commands[$name] = $command;
} else {
$this->aliases[$name] = $command;
}
$names[] = $name;
}
$this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names];
}
}
private function sortCommands(array $commands): array
{
$namespacedCommands = [];
$globalCommands = [];
foreach ($commands as $name => $command) {
$key = $this->application->extractNamespace($name, 1);
if (!$key) {
$globalCommands['_global'][$name] = $command;
} else {
$namespacedCommands[$key][$name] = $command;
}
}
ksort($namespacedCommands);
$namespacedCommands = array_merge($globalCommands, $namespacedCommands);
foreach ($namespacedCommands as &$commandsSet) {
ksort($commandsSet);
}
// unset reference to keep scope clear
unset($commandsSet);
return $namespacedCommands;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Descriptor interface.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
interface DescriptorInterface
{
/**
* Describes an object if supported.
*
* @param object $object
*/
public function describe(OutputInterface $output, $object, array $options = []);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*
* @internal
*/
abstract class Descriptor implements DescriptorInterface
{
/**
* @var OutputInterface
*/
protected $output;
/**
* {@inheritdoc}
*/
public function describe(OutputInterface $output, $object, array $options = [])
{
$this->output = $output;
switch (true) {
case $object instanceof InputArgument:
$this->describeInputArgument($object, $options);
break;
case $object instanceof InputOption:
$this->describeInputOption($object, $options);
break;
case $object instanceof InputDefinition:
$this->describeInputDefinition($object, $options);
break;
case $object instanceof Command:
$this->describeCommand($object, $options);
break;
case $object instanceof Application:
$this->describeApplication($object, $options);
break;
default:
throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', \get_class($object)));
}
}
/**
* Writes content to output.
*
* @param string $content
* @param bool $decorated
*/
protected function write($content, $decorated = false)
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}
/**
* Describes an InputArgument instance.
*
* @return string|mixed
*/
abstract protected function describeInputArgument(InputArgument $argument, array $options = []);
/**
* Describes an InputOption instance.
*
* @return string|mixed
*/
abstract protected function describeInputOption(InputOption $option, array $options = []);
/**
* Describes an InputDefinition instance.
*
* @return string|mixed
*/
abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []);
/**
* Describes a Command instance.
*
* @return string|mixed
*/
abstract protected function describeCommand(Command $command, array $options = []);
/**
* Describes an Application instance.
*
* @return string|mixed
*/
abstract protected function describeApplication(Application $application, array $options = []);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
/**
* JSON descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @internal
*/
class JsonDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->writeData($this->getInputArgumentData($argument), $options);
}
/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = [])
{
$this->writeData($this->getInputOptionData($option), $options);
}
/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$this->writeData($this->getInputDefinitionData($definition), $options);
}
/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = [])
{
$this->writeData($this->getCommandData($command), $options);
}
/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$description = new ApplicationDescription($application, $describedNamespace, true);
$commands = [];
foreach ($description->getCommands() as $command) {
$commands[] = $this->getCommandData($command);
}
$data = [];
if ('UNKNOWN' !== $application->getName()) {
$data['application']['name'] = $application->getName();
if ('UNKNOWN' !== $application->getVersion()) {
$data['application']['version'] = $application->getVersion();
}
}
$data['commands'] = $commands;
if ($describedNamespace) {
$data['namespace'] = $describedNamespace;
} else {
$data['namespaces'] = array_values($description->getNamespaces());
}
$this->writeData($data, $options);
}
/**
* Writes data as json.
*/
private function writeData(array $data, array $options)
{
$flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0;
$this->write(json_encode($data, $flags));
}
/**
* @return array
*/
private function getInputArgumentData(InputArgument $argument)
{
return [
'name' => $argument->getName(),
'is_required' => $argument->isRequired(),
'is_array' => $argument->isArray(),
'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()),
'default' => INF === $argument->getDefault() ? 'INF' : $argument->getDefault(),
];
}
/**
* @return array
*/
private function getInputOptionData(InputOption $option)
{
return [
'name' => '--'.$option->getName(),
'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '',
'accept_value' => $option->acceptValue(),
'is_value_required' => $option->isValueRequired(),
'is_multiple' => $option->isArray(),
'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()),
'default' => INF === $option->getDefault() ? 'INF' : $option->getDefault(),
];
}
/**
* @return array
*/
private function getInputDefinitionData(InputDefinition $definition)
{
$inputArguments = [];
foreach ($definition->getArguments() as $name => $argument) {
$inputArguments[$name] = $this->getInputArgumentData($argument);
}
$inputOptions = [];
foreach ($definition->getOptions() as $name => $option) {
$inputOptions[$name] = $this->getInputOptionData($option);
}
return ['arguments' => $inputArguments, 'options' => $inputOptions];
}
/**
* @return array
*/
private function getCommandData(Command $command)
{
$command->getSynopsis();
$command->mergeApplicationDefinition(false);
return [
'name' => $command->getName(),
'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()),
'description' => $command->getDescription(),
'help' => $command->getProcessedHelp(),
'definition' => $this->getInputDefinitionData($command->getNativeDefinition()),
'hidden' => $command->isHidden(),
];
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Markdown descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @internal
*/
class MarkdownDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
public function describe(OutputInterface $output, $object, array $options = [])
{
$decorated = $output->isDecorated();
$output->setDecorated(false);
parent::describe($output, $object, $options);
$output->setDecorated($decorated);
}
/**
* {@inheritdoc}
*/
protected function write($content, $decorated = true)
{
parent::write($content, $decorated);
}
/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->write(
'#### `'.($argument->getName() ?: '<none>')."`\n\n"
.($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
.'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
.'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
.'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
);
}
/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = [])
{
$name = '--'.$option->getName();
if ($option->getShortcut()) {
$name .= '|-'.str_replace('|', '|-', $option->getShortcut()).'';
}
$this->write(
'#### `'.$name.'`'."\n\n"
.($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '')
.'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
.'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
.'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
.'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
);
}
/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
if ($showArguments = \count($definition->getArguments()) > 0) {
$this->write('### Arguments');
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
$this->write($this->describeInputArgument($argument));
}
}
if (\count($definition->getOptions()) > 0) {
if ($showArguments) {
$this->write("\n\n");
}
$this->write('### Options');
foreach ($definition->getOptions() as $option) {
$this->write("\n\n");
$this->write($this->describeInputOption($option));
}
}
}
/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = [])
{
$command->getSynopsis();
$command->mergeApplicationDefinition(false);
$this->write(
'`'.$command->getName()."`\n"
.str_repeat('-', Helper::strlen($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
);
if ($help = $command->getProcessedHelp()) {
$this->write("\n");
$this->write($help);
}
if ($command->getNativeDefinition()) {
$this->write("\n\n");
$this->describeInputDefinition($command->getNativeDefinition());
}
}
/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = [])
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$description = new ApplicationDescription($application, $describedNamespace);
$title = $this->getApplicationTitle($application);
$this->write($title."\n".str_repeat('=', Helper::strlen($title)));
foreach ($description->getNamespaces() as $namespace) {
if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->write("\n\n");
$this->write('**'.$namespace['id'].':**');
}
$this->write("\n\n");
$this->write(implode("\n", array_map(function ($commandName) use ($description) {
return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName()));
}, $namespace['commands'])));
}
foreach ($description->getCommands() as $command) {
$this->write("\n\n");
$this->write($this->describeCommand($command));
}
}
private function getApplicationTitle(Application $application)
{
if ('UNKNOWN' !== $application->getName()) {
if ('UNKNOWN' !== $application->getVersion()) {
return sprintf('%s %s', $application->getName(), $application->getVersion());
}
return $application->getName();
}
return 'Console Tool';
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
/**
* XML descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @internal
*/
class XmlDescriptor extends Descriptor
{
/**
* @return \DOMDocument
*/
public function getInputDefinitionDocument(InputDefinition $definition)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($definitionXML = $dom->createElement('definition'));
$definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
foreach ($definition->getArguments() as $argument) {
$this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument));
}
$definitionXML->appendChild($optionsXML = $dom->createElement('options'));
foreach ($definition->getOptions() as $option) {
$this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
}
return $dom;
}
/**
* @return \DOMDocument
*/
public function getCommandDocument(Command $command)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($commandXML = $dom->createElement('command'));
$command->getSynopsis();
$command->mergeApplicationDefinition(false);
$commandXML->setAttribute('id', $command->getName());
$commandXML->setAttribute('name', $command->getName());
$commandXML->setAttribute('hidden', $command->isHidden() ? 1 : 0);
$commandXML->appendChild($usagesXML = $dom->createElement('usages'));
foreach (array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()) as $usage) {
$usagesXML->appendChild($dom->createElement('usage', $usage));
}
$commandXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription())));
$commandXML->appendChild($helpXML = $dom->createElement('help'));
$helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp())));
$definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition());
$this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0));
return $dom;
}
/**
* @param string|null $namespace
*
* @return \DOMDocument
*/
public function getApplicationDocument(Application $application, $namespace = null)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($rootXml = $dom->createElement('symfony'));
if ('UNKNOWN' !== $application->getName()) {
$rootXml->setAttribute('name', $application->getName());
if ('UNKNOWN' !== $application->getVersion()) {
$rootXml->setAttribute('version', $application->getVersion());
}
}
$rootXml->appendChild($commandsXML = $dom->createElement('commands'));
$description = new ApplicationDescription($application, $namespace, true);
if ($namespace) {
$commandsXML->setAttribute('namespace', $namespace);
}
foreach ($description->getCommands() as $command) {
$this->appendDocument($commandsXML, $this->getCommandDocument($command));
}
if (!$namespace) {
$rootXml->appendChild($namespacesXML = $dom->createElement('namespaces'));
foreach ($description->getNamespaces() as $namespaceDescription) {
$namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace'));
$namespaceArrayXML->setAttribute('id', $namespaceDescription['id']);
foreach ($namespaceDescription['commands'] as $name) {
$namespaceArrayXML->appendChild($commandXML = $dom->createElement('command'));
$commandXML->appendChild($dom->createTextNode($name));
}
}
}
return $dom;
}
/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
$this->writeDocument($this->getInputArgumentDocument($argument));
}
/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = [])
{
$this->writeDocument($this->getInputOptionDocument($option));
}
/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$this->writeDocument($this->getInputDefinitionDocument($definition));
}
/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = [])
{
$this->writeDocument($this->getCommandDocument($command));
}
/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = [])
{
$this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null));
}
/**
* Appends document children to parent node.
*/
private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent)
{
foreach ($importedParent->childNodes as $childNode) {
$parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
}
}
/**
* Writes DOM document.
*/
private function writeDocument(\DOMDocument $dom)
{
$dom->formatOutput = true;
$this->write($dom->saveXML());
}
private function getInputArgumentDocument(InputArgument $argument): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($objectXML = $dom->createElement('argument'));
$objectXML->setAttribute('name', $argument->getName());
$objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
$objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));
$objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
$defaults = \is_array($argument->getDefault()) ? $argument->getDefault() : (\is_bool($argument->getDefault()) ? [var_export($argument->getDefault(), true)] : ($argument->getDefault() ? [$argument->getDefault()] : []));
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
return $dom;
}
private function getInputOptionDocument(InputOption $option): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($objectXML = $dom->createElement('option'));
$objectXML->setAttribute('name', '--'.$option->getName());
$pos = strpos($option->getShortcut(), '|');
if (false !== $pos) {
$objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
$objectXML->setAttribute('shortcuts', '-'.str_replace('|', '|-', $option->getShortcut()));
} else {
$objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
}
$objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
$objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
$objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
if ($option->acceptValue()) {
$defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : []));
$objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
if (!empty($defaults)) {
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
}
}
return $dom;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Question;
/**
* Represents a yes/no question.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConfirmationQuestion extends Question
{
private $trueAnswerRegex;
/**
* @param string $question The question to ask to the user
* @param bool $default The default answer to return, true or false
* @param string $trueAnswerRegex A regex to match the "yes" answer
*/
public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i')
{
parent::__construct($question, $default);
$this->trueAnswerRegex = $trueAnswerRegex;
$this->setNormalizer($this->getDefaultNormalizer());
}
/**
* Returns the default answer normalizer.
*
* @return callable
*/
private function getDefaultNormalizer()
{
$default = $this->getDefault();
$regex = $this->trueAnswerRegex;
return function ($answer) use ($default, $regex) {
if (\is_bool($answer)) {
return $answer;
}
$answerIsTrue = (bool) preg_match($regex, $answer);
if (false === $default) {
return $answer && $answerIsTrue;
}
return '' === $answer || $answerIsTrue;
};
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Question;
use Symfony\Component\Console\Exception\InvalidArgumentException;
/**
* Represents a choice question.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ChoiceQuestion extends Question
{
private $choices;
private $multiselect = false;
private $prompt = ' > ';
private $errorMessage = 'Value "%s" is invalid';
/**
* @param string $question The question to ask to the user
* @param array $choices The list of available choices
* @param mixed $default The default answer to return
*/
public function __construct(string $question, array $choices, $default = null)
{
if (!$choices) {
throw new \LogicException('Choice question must have at least 1 choice available.');
}
parent::__construct($question, $default);
$this->choices = $choices;
$this->setValidator($this->getDefaultValidator());
$this->setAutocompleterValues($choices);
}
/**
* Returns available choices.
*
* @return array
*/
public function getChoices()
{
return $this->choices;
}
/**
* Sets multiselect option.
*
* When multiselect is set to true, multiple choices can be answered.
*
* @param bool $multiselect
*
* @return $this
*/
public function setMultiselect($multiselect)
{
$this->multiselect = $multiselect;
$this->setValidator($this->getDefaultValidator());
return $this;
}
/**
* Returns whether the choices are multiselect.
*
* @return bool
*/
public function isMultiselect()
{
return $this->multiselect;
}
/**
* Gets the prompt for choices.
*
* @return string
*/
public function getPrompt()
{
return $this->prompt;
}
/**
* Sets the prompt for choices.
*
* @param string $prompt
*
* @return $this
*/
public function setPrompt($prompt)
{
$this->prompt = $prompt;
return $this;
}
/**
* Sets the error message for invalid values.
*
* The error message has a string placeholder (%s) for the invalid value.
*
* @param string $errorMessage
*
* @return $this
*/
public function setErrorMessage($errorMessage)
{
$this->errorMessage = $errorMessage;
$this->setValidator($this->getDefaultValidator());
return $this;
}
private function getDefaultValidator(): callable
{
$choices = $this->choices;
$errorMessage = $this->errorMessage;
$multiselect = $this->multiselect;
$isAssoc = $this->isAssoc($choices);
return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
if ($multiselect) {
// Check for a separated comma values
if (!preg_match('/^[^,]+(?:,[^,]+)*$/', $selected, $matches)) {
throw new InvalidArgumentException(sprintf($errorMessage, $selected));
}
$selectedChoices = array_map('trim', explode(',', $selected));
} else {
$selectedChoices = [trim($selected)];
}
$multiselectChoices = [];
foreach ($selectedChoices as $value) {
$results = [];
foreach ($choices as $key => $choice) {
if ($choice === $value) {
$results[] = $key;
}
}
if (\count($results) > 1) {
throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results)));
}
$result = array_search($value, $choices);
if (!$isAssoc) {
if (false !== $result) {
$result = $choices[$result];
} elseif (isset($choices[$value])) {
$result = $choices[$value];
}
} elseif (false === $result && isset($choices[$value])) {
$result = $value;
}
if (false === $result) {
throw new InvalidArgumentException(sprintf($errorMessage, $value));
}
$multiselectChoices[] = (string) $result;
}
if ($multiselect) {
return $multiselectChoices;
}
return current($multiselectChoices);
};
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Question;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
/**
* Represents a Question.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Question
{
private $question;
private $attempts;
private $hidden = false;
private $hiddenFallback = true;
private $autocompleterCallback;
private $validator;
private $default;
private $normalizer;
/**
* @param string $question The question to ask to the user
* @param mixed $default The default answer to return if the user enters nothing
*/
public function __construct(string $question, $default = null)
{
$this->question = $question;
$this->default = $default;
}
/**
* Returns the question.
*
* @return string
*/
public function getQuestion()
{
return $this->question;
}
/**
* Returns the default answer.
*
* @return mixed
*/
public function getDefault()
{
return $this->default;
}
/**
* Returns whether the user response must be hidden.
*
* @return bool
*/
public function isHidden()
{
return $this->hidden;
}
/**
* Sets whether the user response must be hidden or not.
*
* @param bool $hidden
*
* @return $this
*
* @throws LogicException In case the autocompleter is also used
*/
public function setHidden($hidden)
{
if ($this->autocompleterCallback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
}
$this->hidden = (bool) $hidden;
return $this;
}
/**
* In case the response can not be hidden, whether to fallback on non-hidden question or not.
*
* @return bool
*/
public function isHiddenFallback()
{
return $this->hiddenFallback;
}
/**
* Sets whether to fallback on non-hidden question if the response can not be hidden.
*
* @param bool $fallback
*
* @return $this
*/
public function setHiddenFallback($fallback)
{
$this->hiddenFallback = (bool) $fallback;
return $this;
}
/**
* Gets values for the autocompleter.
*
* @return iterable|null
*/
public function getAutocompleterValues()
{
$callback = $this->getAutocompleterCallback();
return $callback ? $callback('') : null;
}
/**
* Sets values for the autocompleter.
*
* @param iterable|null $values
*
* @return $this
*
* @throws InvalidArgumentException
* @throws LogicException
*/
public function setAutocompleterValues($values)
{
if (\is_array($values)) {
$values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
$callback = static function () use ($values) {
return $values;
};
} elseif ($values instanceof \Traversable) {
$valueCache = null;
$callback = static function () use ($values, &$valueCache) {
return $valueCache ?? $valueCache = iterator_to_array($values, false);
};
} elseif (null === $values) {
$callback = null;
} else {
throw new InvalidArgumentException('Autocompleter values can be either an array, "null" or a "Traversable" object.');
}
return $this->setAutocompleterCallback($callback);
}
/**
* Gets the callback function used for the autocompleter.
*/
public function getAutocompleterCallback(): ?callable
{
return $this->autocompleterCallback;
}
/**
* Sets the callback function used for the autocompleter.
*
* The callback is passed the user input as argument and should return an iterable of corresponding suggestions.
*
* @return $this
*/
public function setAutocompleterCallback(callable $callback = null): self
{
if ($this->hidden && null !== $callback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
}
$this->autocompleterCallback = $callback;
return $this;
}
/**
* Sets a validator for the question.
*
* @return $this
*/
public function setValidator(callable $validator = null)
{
$this->validator = $validator;
return $this;
}
/**
* Gets the validator for the question.
*
* @return callable|null
*/
public function getValidator()
{
return $this->validator;
}
/**
* Sets the maximum number of attempts.
*
* Null means an unlimited number of attempts.
*
* @param int|null $attempts
*
* @return $this
*
* @throws InvalidArgumentException in case the number of attempts is invalid
*/
public function setMaxAttempts($attempts)
{
if (null !== $attempts && $attempts < 1) {
throw new InvalidArgumentException('Maximum number of attempts must be a positive value.');
}
$this->attempts = $attempts;
return $this;
}
/**
* Gets the maximum number of attempts.
*
* Null means an unlimited number of attempts.
*
* @return int|null
*/
public function getMaxAttempts()
{
return $this->attempts;
}
/**
* Sets a normalizer for the response.
*
* The normalizer can be a callable (a string), a closure or a class implementing __invoke.
*
* @return $this
*/
public function setNormalizer(callable $normalizer)
{
$this->normalizer = $normalizer;
return $this;
}
/**
* Gets the normalizer for the response.
*
* The normalizer can ba a callable (a string), a closure or a class implementing __invoke.
*
* @return callable|null
*/
public function getNormalizer()
{
return $this->normalizer;
}
protected function isAssoc($array)
{
return (bool) \count(array_filter(array_keys($array), 'is_string'));
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Comparator;
/**
* NumberComparator compiles a simple comparison to an anonymous
* subroutine, which you can call with a value to be tested again.
*
* Now this would be very pointless, if NumberCompare didn't understand
* magnitudes.
*
* The target value may use magnitudes of kilobytes (k, ki),
* megabytes (m, mi), or gigabytes (g, gi). Those suffixed
* with an i use the appropriate 2**n version in accordance with the
* IEC standard: http://physics.nist.gov/cuu/Units/binary.html
*
* Based on the Perl Number::Compare module.
*
* @author Fabien Potencier <fabien@symfony.com> PHP port
* @author Richard Clamp <richardc@unixbeard.net> Perl version
* @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
*
* @see http://physics.nist.gov/cuu/Units/binary.html
*/
class NumberComparator extends Comparator
{
/**
* @param string|int $test A comparison string or an integer
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function __construct(?string $test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test));
}
$target = $matches[2];
if (!is_numeric($target)) {
throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target));
}
if (isset($matches[3])) {
// magnitude
switch (strtolower($matches[3])) {
case 'k':
$target *= 1000;
break;
case 'ki':
$target *= 1024;
break;
case 'm':
$target *= 1000000;
break;
case 'mi':
$target *= 1024 * 1024;
break;
case 'g':
$target *= 1000000000;
break;
case 'gi':
$target *= 1024 * 1024 * 1024;
break;
}
}
$this->setTarget($target);
$this->setOperator(isset($matches[1]) ? $matches[1] : '==');
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Comparator;
/**
* Comparator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Comparator
{
private $target;
private $operator = '==';
/**
* Gets the target value.
*
* @return string The target value
*/
public function getTarget()
{
return $this->target;
}
/**
* Sets the target value.
*
* @param string $target The target value
*/
public function setTarget($target)
{
$this->target = $target;
}
/**
* Gets the comparison operator.
*
* @return string The operator
*/
public function getOperator()
{
return $this->operator;
}
/**
* Sets the comparison operator.
*
* @param string $operator A valid operator
*
* @throws \InvalidArgumentException
*/
public function setOperator($operator)
{
if (!$operator) {
$operator = '==';
}
if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) {
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
}
$this->operator = $operator;
}
/**
* Tests against the target.
*
* @param mixed $test A test value
*
* @return bool
*/
public function test($test)
{
switch ($this->operator) {
case '>':
return $test > $this->target;
case '>=':
return $test >= $this->target;
case '<':
return $test < $this->target;
case '<=':
return $test <= $this->target;
case '!=':
return $test != $this->target;
}
return $test == $this->target;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Comparator;
/**
* DateCompare compiles date comparisons.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DateComparator extends Comparator
{
/**
* @param string $test A comparison string
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function __construct(string $test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
}
try {
$date = new \DateTime($matches[2]);
$target = $date->format('U');
} catch (\Exception $e) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
}
$operator = isset($matches[1]) ? $matches[1] : '==';
if ('since' === $operator || 'after' === $operator) {
$operator = '>';
}
if ('until' === $operator || 'before' === $operator) {
$operator = '<';
}
$this->setOperator($operator);
$this->setTarget($target);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Exception;
/**
* @author Andreas Erhard <andreas.erhard@i-med.ac.at>
*/
class DirectoryNotFoundException extends \InvalidArgumentException
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Exception;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class AccessDeniedException extends \UnexpectedValueException
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder;
/**
* Gitignore matches against text.
*
* @author Ahmed Abdou <mail@ahmd.io>
*/
class Gitignore
{
/**
* Returns a regexp which is the equivalent of the gitignore pattern.
*
* @return string The regexp
*/
public static function toRegex(string $gitignoreFileContent): string
{
$gitignoreFileContent = preg_replace('/^[^\\\r\n]*#.*/m', '', $gitignoreFileContent);
$gitignoreLines = preg_split('/\r\n|\r|\n/', $gitignoreFileContent);
$gitignoreLines = array_map('trim', $gitignoreLines);
$gitignoreLines = array_filter($gitignoreLines);
$ignoreLinesPositive = array_filter($gitignoreLines, function (string $line) {
return !preg_match('/^!/', $line);
});
$ignoreLinesNegative = array_filter($gitignoreLines, function (string $line) {
return preg_match('/^!/', $line);
});
$ignoreLinesNegative = array_map(function (string $line) {
return preg_replace('/^!(.*)/', '${1}', $line);
}, $ignoreLinesNegative);
$ignoreLinesNegative = array_map([__CLASS__, 'getRegexFromGitignore'], $ignoreLinesNegative);
$ignoreLinesPositive = array_map([__CLASS__, 'getRegexFromGitignore'], $ignoreLinesPositive);
if (empty($ignoreLinesPositive)) {
return '/^$/';
}
if (empty($ignoreLinesNegative)) {
return sprintf('/%s/', implode('|', $ignoreLinesPositive));
}
return sprintf('/(?=^(?:(?!(%s)).)*$)(%s)/', implode('|', $ignoreLinesNegative), implode('|', $ignoreLinesPositive));
}
private static function getRegexFromGitignore(string $gitignorePattern): string
{
$regex = '(';
if (0 === strpos($gitignorePattern, '/')) {
$gitignorePattern = substr($gitignorePattern, 1);
$regex .= '^';
} else {
$regex .= '(^|\/)';
}
if ('/' === $gitignorePattern[\strlen($gitignorePattern) - 1]) {
$gitignorePattern = substr($gitignorePattern, 0, -1);
}
$iMax = \strlen($gitignorePattern);
for ($i = 0; $i < $iMax; ++$i) {
$doubleChars = substr($gitignorePattern, $i, 2);
if ('**' === $doubleChars) {
$regex .= '.+';
++$i;
continue;
}
$c = $gitignorePattern[$i];
switch ($c) {
case '*':
$regex .= '[^\/]+';
break;
case '/':
case '.':
case ':':
case '(':
case ')':
case '{':
case '}':
$regex .= '\\'.$c;
break;
default:
$regex .= $c;
}
}
$regex .= '($|\/)';
$regex .= ')';
return $regex;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder;
/**
* Extends \SplFileInfo to support relative paths.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SplFileInfo extends \SplFileInfo
{
private $relativePath;
private $relativePathname;
/**
* @param string $file The file name
* @param string $relativePath The relative path
* @param string $relativePathname The relative path name
*/
public function __construct(string $file, string $relativePath, string $relativePathname)
{
parent::__construct($file);
$this->relativePath = $relativePath;
$this->relativePathname = $relativePathname;
}
/**
* Returns the relative path.
*
* This path does not contain the file name.
*
* @return string the relative path
*/
public function getRelativePath()
{
return $this->relativePath;
}
/**
* Returns the relative path name.
*
* This path contains the file name.
*
* @return string the relative path name
*/
public function getRelativePathname()
{
return $this->relativePathname;
}
public function getFilenameWithoutExtension(): string
{
$filename = $this->getFilename();
return pathinfo($filename, PATHINFO_FILENAME);
}
/**
* Returns the contents of the file.
*
* @return string the contents of the file
*
* @throws \RuntimeException
*/
public function getContents()
{
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
$content = file_get_contents($this->getPathname());
restore_error_handler();
if (false === $content) {
throw new \RuntimeException($error);
}
return $content;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder;
use Symfony\Component\Finder\Comparator\DateComparator;
use Symfony\Component\Finder\Comparator\NumberComparator;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
use Symfony\Component\Finder\Iterator\CustomFilterIterator;
use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
use Symfony\Component\Finder\Iterator\SortableIterator;
/**
* Finder allows to build rules to find files and directories.
*
* It is a thin wrapper around several specialized iterator classes.
*
* All rules may be invoked several times.
*
* All methods return the current Finder object to allow chaining:
*
* $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Finder implements \IteratorAggregate, \Countable
{
const IGNORE_VCS_FILES = 1;
const IGNORE_DOT_FILES = 2;
const IGNORE_VCS_IGNORED_FILES = 4;
private $mode = 0;
private $names = [];
private $notNames = [];
private $exclude = [];
private $filters = [];
private $depths = [];
private $sizes = [];
private $followLinks = false;
private $reverseSorting = false;
private $sort = false;
private $ignore = 0;
private $dirs = [];
private $dates = [];
private $iterators = [];
private $contains = [];
private $notContains = [];
private $paths = [];
private $notPaths = [];
private $ignoreUnreadableDirs = false;
private static $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
public function __construct()
{
$this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
}
/**
* Creates a new Finder.
*
* @return static
*/
public static function create()
{
return new static();
}
/**
* Restricts the matching to directories only.
*
* @return $this
*/
public function directories()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
return $this;
}
/**
* Restricts the matching to files only.
*
* @return $this
*/
public function files()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
return $this;
}
/**
* Adds tests for the directory depth.
*
* Usage:
*
* $finder->depth('> 1') // the Finder will start matching at level 1.
* $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
* $finder->depth(['>= 1', '< 3'])
*
* @param string|int|string[]|int[] $levels The depth level expression or an array of depth levels
*
* @return $this
*
* @see DepthRangeFilterIterator
* @see NumberComparator
*/
public function depth($levels)
{
foreach ((array) $levels as $level) {
$this->depths[] = new Comparator\NumberComparator($level);
}
return $this;
}
/**
* Adds tests for file dates (last modified).
*
* The date must be something that strtotime() is able to parse:
*
* $finder->date('since yesterday');
* $finder->date('until 2 days ago');
* $finder->date('> now - 2 hours');
* $finder->date('>= 2005-10-15');
* $finder->date(['>= 2005-10-15', '<= 2006-05-27']);
*
* @param string|string[] $dates A date range string or an array of date ranges
*
* @return $this
*
* @see strtotime
* @see DateRangeFilterIterator
* @see DateComparator
*/
public function date($dates)
{
foreach ((array) $dates as $date) {
$this->dates[] = new Comparator\DateComparator($date);
}
return $this;
}
/**
* Adds rules that files must match.
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('test.php')
* $finder->name(['test.py', 'test.php'])
*
* @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function name($patterns)
{
$this->names = array_merge($this->names, (array) $patterns);
return $this;
}
/**
* Adds rules that files must not match.
*
* @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function notName($patterns)
{
$this->notNames = array_merge($this->notNames, (array) $patterns);
return $this;
}
/**
* Adds tests that file contents must match.
*
* Strings or PCRE patterns can be used:
*
* $finder->contains('Lorem ipsum')
* $finder->contains('/Lorem ipsum/i')
* $finder->contains(['dolor', '/ipsum/i'])
*
* @param string|string[] $patterns A pattern (string or regexp) or an array of patterns
*
* @return $this
*
* @see FilecontentFilterIterator
*/
public function contains($patterns)
{
$this->contains = array_merge($this->contains, (array) $patterns);
return $this;
}
/**
* Adds tests that file contents must not match.
*
* Strings or PCRE patterns can be used:
*
* $finder->notContains('Lorem ipsum')
* $finder->notContains('/Lorem ipsum/i')
* $finder->notContains(['lorem', '/dolor/i'])
*
* @param string|string[] $patterns A pattern (string or regexp) or an array of patterns
*
* @return $this
*
* @see FilecontentFilterIterator
*/
public function notContains($patterns)
{
$this->notContains = array_merge($this->notContains, (array) $patterns);
return $this;
}
/**
* Adds rules that filenames must match.
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $finder->path('some/special/dir')
* $finder->path('/some\/special\/dir/') // same as above
* $finder->path(['some dir', 'another/dir'])
*
* Use only / as dirname separator.
*
* @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function path($patterns)
{
$this->paths = array_merge($this->paths, (array) $patterns);
return $this;
}
/**
* Adds rules that filenames must not match.
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $finder->notPath('some/special/dir')
* $finder->notPath('/some\/special\/dir/') // same as above
* $finder->notPath(['some/file.txt', 'another/file.log'])
*
* Use only / as dirname separator.
*
* @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function notPath($patterns)
{
$this->notPaths = array_merge($this->notPaths, (array) $patterns);
return $this;
}
/**
* Adds tests for file sizes.
*
* $finder->size('> 10K');
* $finder->size('<= 1Ki');
* $finder->size(4);
* $finder->size(['> 10K', '< 20K'])
*
* @param string|int|string[]|int[] $sizes A size range string or an integer or an array of size ranges
*
* @return $this
*
* @see SizeRangeFilterIterator
* @see NumberComparator
*/
public function size($sizes)
{
foreach ((array) $sizes as $size) {
$this->sizes[] = new Comparator\NumberComparator($size);
}
return $this;
}
/**
* Excludes directories.
*
* Directories passed as argument must be relative to the ones defined with the `in()` method. For example:
*
* $finder->in(__DIR__)->exclude('ruby');
*
* @param string|array $dirs A directory path or an array of directories
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function exclude($dirs)
{
$this->exclude = array_merge($this->exclude, (array) $dirs);
return $this;
}
/**
* Excludes "hidden" directories and files (starting with a dot).
*
* This option is enabled by default.
*
* @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function ignoreDotFiles($ignoreDotFiles)
{
if ($ignoreDotFiles) {
$this->ignore |= static::IGNORE_DOT_FILES;
} else {
$this->ignore &= ~static::IGNORE_DOT_FILES;
}
return $this;
}
/**
* Forces the finder to ignore version control directories.
*
* This option is enabled by default.
*
* @param bool $ignoreVCS Whether to exclude VCS files or not
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function ignoreVCS($ignoreVCS)
{
if ($ignoreVCS) {
$this->ignore |= static::IGNORE_VCS_FILES;
} else {
$this->ignore &= ~static::IGNORE_VCS_FILES;
}
return $this;
}
/**
* Forces Finder to obey .gitignore and ignore files based on rules listed there.
*
* This option is disabled by default.
*
* @return $this
*/
public function ignoreVCSIgnored(bool $ignoreVCSIgnored)
{
if ($ignoreVCSIgnored) {
$this->ignore |= static::IGNORE_VCS_IGNORED_FILES;
} else {
$this->ignore &= ~static::IGNORE_VCS_IGNORED_FILES;
}
return $this;
}
/**
* Adds VCS patterns.
*
* @see ignoreVCS()
*
* @param string|string[] $pattern VCS patterns to ignore
*/
public static function addVCSPattern($pattern)
{
foreach ((array) $pattern as $p) {
self::$vcsPatterns[] = $p;
}
self::$vcsPatterns = array_unique(self::$vcsPatterns);
}
/**
* Sorts files and directories by an anonymous function.
*
* The anonymous function receives two \SplFileInfo instances to compare.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sort(\Closure $closure)
{
$this->sort = $closure;
return $this;
}
/**
* Sorts files and directories by name.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @param bool $useNaturalSort Whether to use natural sort or not, disabled by default
*
* @return $this
*
* @see SortableIterator
*/
public function sortByName(/* bool $useNaturalSort = false */)
{
if (\func_num_args() < 1 && __CLASS__ !== \get_class($this) && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
@trigger_error(sprintf('The "%s()" method will have a new "bool $useNaturalSort = false" argument in version 5.0, not defining it is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
}
$useNaturalSort = 0 < \func_num_args() && func_get_arg(0);
$this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME;
return $this;
}
/**
* Sorts files and directories by type (directories before files), then by name.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByType()
{
$this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
return $this;
}
/**
* Sorts files and directories by the last accessed time.
*
* This is the time that the file was last accessed, read or written to.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByAccessedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
return $this;
}
/**
* Reverses the sorting.
*
* @return $this
*/
public function reverseSorting()
{
$this->reverseSorting = true;
return $this;
}
/**
* Sorts files and directories by the last inode changed time.
*
* This is the time that the inode information was last modified (permissions, owner, group or other metadata).
*
* On Windows, since inode is not available, changed time is actually the file creation time.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByChangedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
return $this;
}
/**
* Sorts files and directories by the last modified time.
*
* This is the last time the actual contents of the file were last modified.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByModifiedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
return $this;
}
/**
* Filters the iterator with an anonymous function.
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @return $this
*
* @see CustomFilterIterator
*/
public function filter(\Closure $closure)
{
$this->filters[] = $closure;
return $this;
}
/**
* Forces the following of symlinks.
*
* @return $this
*/
public function followLinks()
{
$this->followLinks = true;
return $this;
}
/**
* Tells finder to ignore unreadable directories.
*
* By default, scanning unreadable directories content throws an AccessDeniedException.
*
* @param bool $ignore
*
* @return $this
*/
public function ignoreUnreadableDirs($ignore = true)
{
$this->ignoreUnreadableDirs = (bool) $ignore;
return $this;
}
/**
* Searches files and directories which match defined rules.
*
* @param string|string[] $dirs A directory path or an array of directories
*
* @return $this
*
* @throws DirectoryNotFoundException if one of the directories does not exist
*/
public function in($dirs)
{
$resolvedDirs = [];
foreach ((array) $dirs as $dir) {
if (is_dir($dir)) {
$resolvedDirs[] = $this->normalizeDir($dir);
} elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR | GLOB_NOSORT)) {
sort($glob);
$resolvedDirs = array_merge($resolvedDirs, array_map([$this, 'normalizeDir'], $glob));
} else {
throw new DirectoryNotFoundException(sprintf('The "%s" directory does not exist.', $dir));
}
}
$this->dirs = array_merge($this->dirs, $resolvedDirs);
return $this;
}
/**
* Returns an Iterator for the current Finder configuration.
*
* This method implements the IteratorAggregate interface.
*
* @return \Iterator|SplFileInfo[] An iterator
*
* @throws \LogicException if the in() method has not been called
*/
public function getIterator()
{
if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
}
if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
return $this->searchInDirectory($this->dirs[0]);
}
$iterator = new \AppendIterator();
foreach ($this->dirs as $dir) {
$iterator->append($this->searchInDirectory($dir));
}
foreach ($this->iterators as $it) {
$iterator->append($it);
}
return $iterator;
}
/**
* Appends an existing set of files/directories to the finder.
*
* The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
*
* @param iterable $iterator
*
* @return $this
*
* @throws \InvalidArgumentException when the given argument is not iterable
*/
public function append($iterator)
{
if ($iterator instanceof \IteratorAggregate) {
$this->iterators[] = $iterator->getIterator();
} elseif ($iterator instanceof \Iterator) {
$this->iterators[] = $iterator;
} elseif ($iterator instanceof \Traversable || \is_array($iterator)) {
$it = new \ArrayIterator();
foreach ($iterator as $file) {
$it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
}
$this->iterators[] = $it;
} else {
throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
}
return $this;
}
/**
* Check if the any results were found.
*
* @return bool
*/
public function hasResults()
{
foreach ($this->getIterator() as $_) {
return true;
}
return false;
}
/**
* Counts all the results collected by the iterators.
*
* @return int
*/
public function count()
{
return iterator_count($this->getIterator());
}
private function searchInDirectory(string $dir): \Iterator
{
$exclude = $this->exclude;
$notPaths = $this->notPaths;
if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
$exclude = array_merge($exclude, self::$vcsPatterns);
}
if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
$notPaths[] = '#(^|/)\..+(/|$)#';
}
if (static::IGNORE_VCS_IGNORED_FILES === (static::IGNORE_VCS_IGNORED_FILES & $this->ignore)) {
$gitignoreFilePath = sprintf('%s/.gitignore', $dir);
if (!is_readable($gitignoreFilePath)) {
throw new \RuntimeException(sprintf('The "ignoreVCSIgnored" option cannot be used by the Finder as the "%s" file is not readable.', $gitignoreFilePath));
}
$notPaths = array_merge($notPaths, [Gitignore::toRegex(file_get_contents($gitignoreFilePath))]);
}
$minDepth = 0;
$maxDepth = PHP_INT_MAX;
foreach ($this->depths as $comparator) {
switch ($comparator->getOperator()) {
case '>':
$minDepth = $comparator->getTarget() + 1;
break;
case '>=':
$minDepth = $comparator->getTarget();
break;
case '<':
$maxDepth = $comparator->getTarget() - 1;
break;
case '<=':
$maxDepth = $comparator->getTarget();
break;
default:
$minDepth = $maxDepth = $comparator->getTarget();
}
}
$flags = \RecursiveDirectoryIterator::SKIP_DOTS;
if ($this->followLinks) {
$flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
}
$iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
if ($exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
}
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
$iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
}
if ($this->mode) {
$iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
}
if ($this->names || $this->notNames) {
$iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
}
if ($this->contains || $this->notContains) {
$iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
}
if ($this->sizes) {
$iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
}
if ($this->dates) {
$iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
}
if ($this->filters) {
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if ($this->paths || $notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
}
if ($this->sort || $this->reverseSorting) {
$iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting);
$iterator = $iteratorAggregate->getIterator();
}
return $iterator;
}
/**
* Normalizes given directory names by removing trailing slashes.
*
* Excluding: (s)ftp:// wrapper
*
* @param string $dir
*
* @return string
*/
private function normalizeDir($dir)
{
$dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
if (preg_match('#^s?ftp://#', $dir)) {
$dir .= '/';
}
return $dir;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Comparator\NumberComparator;
/**
* SizeRangeFilterIterator filters out files that are not in the given size range.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SizeRangeFilterIterator extends \FilterIterator
{
private $comparators = [];
/**
* @param \Iterator $iterator The Iterator to filter
* @param NumberComparator[] $comparators An array of NumberComparator instances
*/
public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (!$fileinfo->isFile()) {
return true;
}
$filesize = $fileinfo->getSize();
foreach ($this->comparators as $compare) {
if (!$compare->test($filesize)) {
return false;
}
}
return true;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* CustomFilterIterator filters files by applying anonymous functions.
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CustomFilterIterator extends \FilterIterator
{
private $filters = [];
/**
* @param \Iterator $iterator The Iterator to filter
* @param callable[] $filters An array of PHP callbacks
*
* @throws \InvalidArgumentException
*/
public function __construct(\Iterator $iterator, array $filters)
{
foreach ($filters as $filter) {
if (!\is_callable($filter)) {
throw new \InvalidArgumentException('Invalid PHP callback.');
}
}
$this->filters = $filters;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
foreach ($this->filters as $filter) {
if (false === $filter($fileinfo)) {
return false;
}
}
return true;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* DepthRangeFilterIterator limits the directory depth.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DepthRangeFilterIterator extends \FilterIterator
{
private $minDepth = 0;
/**
* @param \RecursiveIteratorIterator $iterator The Iterator to filter
* @param int $minDepth The min depth
* @param int $maxDepth The max depth
*/
public function __construct(\RecursiveIteratorIterator $iterator, int $minDepth = 0, int $maxDepth = PHP_INT_MAX)
{
$this->minDepth = $minDepth;
$iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
return $this->getInnerIterator()->getDepth() >= $this->minDepth;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Comparator\DateComparator;
/**
* DateRangeFilterIterator filters out files that are not in the given date range (last modified dates).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DateRangeFilterIterator extends \FilterIterator
{
private $comparators = [];
/**
* @param \Iterator $iterator The Iterator to filter
* @param DateComparator[] $comparators An array of DateComparator instances
*/
public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (!file_exists($fileinfo->getPathname())) {
return false;
}
$filedate = $fileinfo->getMTime();
foreach ($this->comparators as $compare) {
if (!$compare->test($filedate)) {
return false;
}
}
return true;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class MultiplePcreFilterIterator extends \FilterIterator
{
protected $matchRegexps = [];
protected $noMatchRegexps = [];
/**
* @param \Iterator $iterator The Iterator to filter
* @param array $matchPatterns An array of patterns that need to match
* @param array $noMatchPatterns An array of patterns that need to not match
*/
public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
{
foreach ($matchPatterns as $pattern) {
$this->matchRegexps[] = $this->toRegex($pattern);
}
foreach ($noMatchPatterns as $pattern) {
$this->noMatchRegexps[] = $this->toRegex($pattern);
}
parent::__construct($iterator);
}
/**
* Checks whether the string is accepted by the regex filters.
*
* If there is no regexps defined in the class, this method will accept the string.
* Such case can be handled by child classes before calling the method if they want to
* apply a different behavior.
*
* @param string $string The string to be matched against filters
*
* @return bool
*/
protected function isAccepted($string)
{
// should at least not match one rule to exclude
foreach ($this->noMatchRegexps as $regex) {
if (preg_match($regex, $string)) {
return false;
}
}
// should at least match one rule
if ($this->matchRegexps) {
foreach ($this->matchRegexps as $regex) {
if (preg_match($regex, $string)) {
return true;
}
}
return false;
}
// If there is no match rules, the file is accepted
return true;
}
/**
* Checks whether the string is a regex.
*
* @param string $str
*
* @return bool Whether the given string is a regex
*/
protected function isRegex($str)
{
if (preg_match('/^(.{3,}?)[imsxuADU]*$/', $str, $m)) {
$start = substr($m[1], 0, 1);
$end = substr($m[1], -1);
if ($start === $end) {
return !preg_match('/[*?[:alnum:] \\\\]/', $start);
}
foreach ([['{', '}'], ['(', ')'], ['[', ']'], ['<', '>']] as $delimiters) {
if ($start === $delimiters[0] && $end === $delimiters[1]) {
return true;
}
}
}
return false;
}
/**
* Converts string into regexp.
*
* @param string $str Pattern
*
* @return string regexp corresponding to a given string
*/
abstract protected function toRegex($str);
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Exception\AccessDeniedException;
use Symfony\Component\Finder\SplFileInfo;
/**
* Extends the \RecursiveDirectoryIterator to support relative paths.
*
* @author Victor Berchet <victor@suumit.com>
*/
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
{
/**
* @var bool
*/
private $ignoreUnreadableDirs;
/**
* @var bool
*/
private $rewindable;
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
private $rootPath;
private $subPath;
private $directorySeparator = '/';
/**
* @throws \RuntimeException
*/
public function __construct(string $path, int $flags, bool $ignoreUnreadableDirs = false)
{
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
throw new \RuntimeException('This iterator only support returning current as fileinfo.');
}
parent::__construct($path, $flags);
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
$this->rootPath = $path;
if ('/' !== \DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
$this->directorySeparator = \DIRECTORY_SEPARATOR;
}
}
/**
* Return an instance of SplFileInfo with support for relative paths.
*
* @return SplFileInfo File information
*/
public function current()
{
// the logic here avoids redoing the same work in all iterations
if (null === $subPathname = $this->subPath) {
$subPathname = $this->subPath = (string) $this->getSubPath();
}
if ('' !== $subPathname) {
$subPathname .= $this->directorySeparator;
}
$subPathname .= $this->getFilename();
return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname);
}
/**
* @return \RecursiveIterator
*
* @throws AccessDeniedException
*/
public function getChildren()
{
try {
$children = parent::getChildren();
if ($children instanceof self) {
// parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore
$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
// performance optimization to avoid redoing the same work in all children
$children->rewindable = &$this->rewindable;
$children->rootPath = $this->rootPath;
}
return $children;
} catch (\UnexpectedValueException $e) {
if ($this->ignoreUnreadableDirs) {
// If directory is unreadable and finder is set to ignore it, a fake empty content is returned.
return new \RecursiveArrayIterator([]);
} else {
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
}
}
/**
* Do nothing for non rewindable stream.
*/
public function rewind()
{
if (false === $this->isRewindable()) {
return;
}
parent::rewind();
}
/**
* Checks if the stream is rewindable.
*
* @return bool true when the stream is rewindable, false otherwise
*/
public function isRewindable()
{
if (null !== $this->rewindable) {
return $this->rewindable;
}
if (false !== $stream = @opendir($this->getPath())) {
$infos = stream_get_meta_data($stream);
closedir($stream);
if ($infos['seekable']) {
return $this->rewindable = true;
}
}
return $this->rewindable = false;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* PathFilterIterator filters files by path patterns (e.g. some/special/dir).
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
*/
class PathFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$filename = $this->current()->getRelativePathname();
if ('\\' === \DIRECTORY_SEPARATOR) {
$filename = str_replace('\\', '/', $filename);
}
return $this->isAccepted($filename);
}
/**
* Converts strings to regexp.
*
* PCRE patterns are left unchanged.
*
* Default conversion:
* 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/'
*
* Use only / as directory separator (on Windows also).
*
* @param string $str Pattern: regexp or dirname
*
* @return string regexp corresponding to a given string or regexp
*/
protected function toRegex($str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* SortableIterator applies a sort on a given Iterator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SortableIterator implements \IteratorAggregate
{
const SORT_BY_NONE = 0;
const SORT_BY_NAME = 1;
const SORT_BY_TYPE = 2;
const SORT_BY_ACCESSED_TIME = 3;
const SORT_BY_CHANGED_TIME = 4;
const SORT_BY_MODIFIED_TIME = 5;
const SORT_BY_NAME_NATURAL = 6;
private $iterator;
private $sort;
/**
* @param \Traversable $iterator The Iterator to filter
* @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback)
*
* @throws \InvalidArgumentException
*/
public function __construct(\Traversable $iterator, $sort, bool $reverseOrder = false)
{
$this->iterator = $iterator;
$order = $reverseOrder ? -1 : 1;
if (self::SORT_BY_NAME === $sort) {
$this->sort = static function ($a, $b) use ($order) {
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_NAME_NATURAL === $sort) {
$this->sort = static function ($a, $b) use ($order) {
return $order * strnatcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_TYPE === $sort) {
$this->sort = static function ($a, $b) use ($order) {
if ($a->isDir() && $b->isFile()) {
return -$order;
} elseif ($a->isFile() && $b->isDir()) {
return $order;
}
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
$this->sort = static function ($a, $b) use ($order) {
return $order * ($a->getATime() - $b->getATime());
};
} elseif (self::SORT_BY_CHANGED_TIME === $sort) {
$this->sort = static function ($a, $b) use ($order) {
return $order * ($a->getCTime() - $b->getCTime());
};
} elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
$this->sort = static function ($a, $b) use ($order) {
return $order * ($a->getMTime() - $b->getMTime());
};
} elseif (self::SORT_BY_NONE === $sort) {
$this->sort = $order;
} elseif (\is_callable($sort)) {
$this->sort = $reverseOrder ? static function ($a, $b) use ($sort) { return -$sort($a, $b); } : $sort;
} else {
throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
}
}
public function getIterator()
{
if (1 === $this->sort) {
return $this->iterator;
}
$array = iterator_to_array($this->iterator, true);
if (-1 === $this->sort) {
$array = array_reverse($array);
} else {
uasort($array, $this->sort);
}
return new \ArrayIterator($array);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Glob;
/**
* FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FilenameFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
return $this->isAccepted($this->current()->getFilename());
}
/**
* Converts glob to regexp.
*
* PCRE patterns are left unchanged.
* Glob strings are transformed with Glob::toRegex().
*
* @param string $str Pattern: glob or regexp
*
* @return string regexp corresponding to a given glob or regexp
*/
protected function toRegex($str)
{
return $this->isRegex($str) ? $str : Glob::toRegex($str);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* ExcludeDirectoryFilterIterator filters out directories.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator
{
private $iterator;
private $isRecursive;
private $excludedDirs = [];
private $excludedPattern;
/**
* @param \Iterator $iterator The Iterator to filter
* @param string[] $directories An array of directories to exclude
*/
public function __construct(\Iterator $iterator, array $directories)
{
$this->iterator = $iterator;
$this->isRecursive = $iterator instanceof \RecursiveIterator;
$patterns = [];
foreach ($directories as $directory) {
$directory = rtrim($directory, '/');
if (!$this->isRecursive || false !== strpos($directory, '/')) {
$patterns[] = preg_quote($directory, '#');
} else {
$this->excludedDirs[$directory] = true;
}
}
if ($patterns) {
$this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';
}
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool True if the value should be kept, false otherwise
*/
public function accept()
{
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
return false;
}
if ($this->excludedPattern) {
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
$path = str_replace('\\', '/', $path);
return !preg_match($this->excludedPattern, $path);
}
return true;
}
public function hasChildren()
{
return $this->isRecursive && $this->iterator->hasChildren();
}
public function getChildren()
{
$children = new self($this->iterator->getChildren(), []);
$children->excludedDirs = $this->excludedDirs;
$children->excludedPattern = $this->excludedPattern;
return $children;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* FilecontentFilterIterator filters files by their contents using patterns (regexps or strings).
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
*/
class FilecontentFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
if (!$this->matchRegexps && !$this->noMatchRegexps) {
return true;
}
$fileinfo = $this->current();
if ($fileinfo->isDir() || !$fileinfo->isReadable()) {
return false;
}
$content = $fileinfo->getContents();
if (!$content) {
return false;
}
return $this->isAccepted($content);
}
/**
* Converts string to regexp if necessary.
*
* @param string $str Pattern: string or regexp
*
* @return string regexp corresponding to a given string or regexp
*/
protected function toRegex($str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* FileTypeFilterIterator only keeps files, directories, or both.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FileTypeFilterIterator extends \FilterIterator
{
const ONLY_FILES = 1;
const ONLY_DIRECTORIES = 2;
private $mode;
/**
* @param \Iterator $iterator The Iterator to filter
* @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES)
*/
public function __construct(\Iterator $iterator, int $mode)
{
$this->mode = $mode;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
return false;
} elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {
return false;
}
return true;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder;
/**
* Glob matches globbing patterns against text.
*
* if match_glob("foo.*", "foo.bar") echo "matched\n";
*
* // prints foo.bar and foo.baz
* $regex = glob_to_regex("foo.*");
* for (['foo.bar', 'foo.baz', 'foo', 'bar'] as $t)
* {
* if (/$regex/) echo "matched: $car\n";
* }
*
* Glob implements glob(3) style matching that can be used to match
* against text, rather than fetching names from a filesystem.
*
* Based on the Perl Text::Glob module.
*
* @author Fabien Potencier <fabien@symfony.com> PHP port
* @author Richard Clamp <richardc@unixbeard.net> Perl version
* @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
*/
class Glob
{
/**
* Returns a regexp which is the equivalent of the glob pattern.
*
* @param string $glob The glob pattern
* @param bool $strictLeadingDot
* @param bool $strictWildcardSlash
* @param string $delimiter Optional delimiter
*
* @return string regex The regexp
*/
public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true, $delimiter = '#')
{
$firstByte = true;
$escaping = false;
$inCurlies = 0;
$regex = '';
$sizeGlob = \strlen($glob);
for ($i = 0; $i < $sizeGlob; ++$i) {
$car = $glob[$i];
if ($firstByte && $strictLeadingDot && '.' !== $car) {
$regex .= '(?=[^\.])';
}
$firstByte = '/' === $car;
if ($firstByte && $strictWildcardSlash && isset($glob[$i + 2]) && '**' === $glob[$i + 1].$glob[$i + 2] && (!isset($glob[$i + 3]) || '/' === $glob[$i + 3])) {
$car = '[^/]++/';
if (!isset($glob[$i + 3])) {
$car .= '?';
}
if ($strictLeadingDot) {
$car = '(?=[^\.])'.$car;
}
$car = '/(?:'.$car.')*';
$i += 2 + isset($glob[$i + 3]);
if ('/' === $delimiter) {
$car = str_replace('/', '\\/', $car);
}
}
if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
$regex .= "\\$car";
} elseif ('*' === $car) {
$regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
} elseif ('?' === $car) {
$regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
} elseif ('{' === $car) {
$regex .= $escaping ? '\\{' : '(';
if (!$escaping) {
++$inCurlies;
}
} elseif ('}' === $car && $inCurlies) {
$regex .= $escaping ? '}' : ')';
if (!$escaping) {
--$inCurlies;
}
} elseif (',' === $car && $inCurlies) {
$regex .= $escaping ? ',' : '|';
} elseif ('\\' === $car) {
if ($escaping) {
$regex .= '\\\\';
$escaping = false;
} else {
$escaping = true;
}
continue;
} else {
$regex .= $car;
}
$escaping = false;
}
return $delimiter.'^'.$regex.'$'.$delimiter;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Ctype;
/**
* Ctype implementation through regex.
*
* @internal
*
* @author Gert de Pagter <BackEndTea@gmail.com>
*/
final class Ctype
{
/**
* Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise.
*
* @see https://php.net/ctype-alnum
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_alnum($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text);
}
/**
* Returns TRUE if every character in text is a letter, FALSE otherwise.
*
* @see https://php.net/ctype-alpha
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_alpha($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text);
}
/**
* Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise.
*
* @see https://php.net/ctype-cntrl
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_cntrl($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text);
}
/**
* Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise.
*
* @see https://php.net/ctype-digit
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_digit($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text);
}
/**
* Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise.
*
* @see https://php.net/ctype-graph
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_graph($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
}
/**
* Returns TRUE if every character in text is a lowercase letter.
*
* @see https://php.net/ctype-lower
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_lower($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text);
}
/**
* Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all.
*
* @see https://php.net/ctype-print
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_print($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
}
/**
* Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise.
*
* @see https://php.net/ctype-punct
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_punct($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
}
/**
* Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters.
*
* @see https://php.net/ctype-space
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_space($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text);
}
/**
* Returns TRUE if every character in text is an uppercase letter.
*
* @see https://php.net/ctype-upper
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_upper($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text);
}
/**
* Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
*
* @see https://php.net/ctype-xdigit
*
* @param string|int $text
*
* @return bool
*/
public static function ctype_xdigit($text)
{
$text = self::convert_int_to_char_for_ctype($text);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text);
}
/**
* Converts integers to their char versions according to normal ctype behaviour, if needed.
*
* If an integer between -128 and 255 inclusive is provided,
* it is interpreted as the ASCII value of a single character
* (negative values have 256 added in order to allow characters in the Extended ASCII range).
* Any other integer is interpreted as a string containing the decimal digits of the integer.
*
* @param string|int $int
*
* @return mixed
*/
private static function convert_int_to_char_for_ctype($int)
{
if (!\is_int($int)) {
return $int;
}
if ($int < -128 || $int > 255) {
return (string) $int;
}
if ($int < 0) {
$int += 256;
}
return \chr($int);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Ctype as p;
if (!function_exists('ctype_alnum')) {
function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); }
function ctype_digit($text) { return p\Ctype::ctype_digit($text); }
function ctype_graph($text) { return p\Ctype::ctype_graph($text); }
function ctype_lower($text) { return p\Ctype::ctype_lower($text); }
function ctype_print($text) { return p\Ctype::ctype_print($text); }
function ctype_punct($text) { return p\Ctype::ctype_punct($text); }
function ctype_space($text) { return p\Ctype::ctype_space($text); }
function ctype_upper($text) { return p\Ctype::ctype_upper($text); }
function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); }
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class JsonException extends Exception
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Php73 as p;
if (PHP_VERSION_ID < 70300) {
if (!function_exists('is_countable')) {
function is_countable($var) { return is_array($var) || $var instanceof Countable || $var instanceof ResourceBundle || $var instanceof SimpleXmlElement; }
}
if (!function_exists('hrtime')) {
require_once __DIR__.'/Php73.php';
p\Php73::$startAt = (int) microtime(true);
function hrtime($asNum = false) { return p\Php73::hrtime($asNum); }
}
if (!function_exists('array_key_first')) {
function array_key_first(array $array) { foreach ($array as $key => $value) { return $key; } }
}
if (!function_exists('array_key_last')) {
function array_key_last(array $array) { end($array); return key($array); }
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Php73;
/**
* @author Gabriel Caruso <carusogabriel34@gmail.com>
* @author Ion Bazan <ion.bazan@gmail.com>
*
* @internal
*/
final class Php73
{
public static $startAt = 1533462603;
/**
* @param bool $asNum
*
* @return array|float|int
*/
public static function hrtime($asNum = false)
{
$ns = microtime(false);
$s = substr($ns, 11) - self::$startAt;
$ns = 1E9 * (float) $ns;
if ($asNum) {
$ns += $s * 1E9;
return \PHP_INT_SIZE === 4 ? $ns : (int) $ns;
}
return array($s, (int) $ns);
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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();
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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));
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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);
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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)
)
);
}
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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)
);
}
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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)
)
);
}
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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.'
);
}
}
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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);
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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';
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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);
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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)
)
);
}
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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);
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php
/*
* This file is part of sebastian/comparator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php
/*
* This file is part of code-unit-reverse-lookup.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeUnitReverseLookup;
/**
* @since Class available since Release 1.0.0
*/
class Wizard
{
/**
* @var array
*/
private $lookupTable = [];
/**
* @var array
*/
private $processedClasses = [];
/**
* @var array
*/
private $processedFunctions = [];
/**
* @param string $filename
* @param int $lineNumber
*
* @return string
*/
public function lookup($filename, $lineNumber)
{
if (!isset($this->lookupTable[$filename][$lineNumber])) {
$this->updateLookupTable();
}
if (isset($this->lookupTable[$filename][$lineNumber])) {
return $this->lookupTable[$filename][$lineNumber];
} else {
return $filename . ':' . $lineNumber;
}
}
private function updateLookupTable()
{
$this->processClassesAndTraits();
$this->processFunctions();
}
private function processClassesAndTraits()
{
foreach (array_merge(get_declared_classes(), get_declared_traits()) as $classOrTrait) {
if (isset($this->processedClasses[$classOrTrait])) {
continue;
}
$reflector = new \ReflectionClass($classOrTrait);
foreach ($reflector->getMethods() as $method) {
$this->processFunctionOrMethod($method);
}
$this->processedClasses[$classOrTrait] = true;
}
}
private function processFunctions()
{
foreach (get_defined_functions()['user'] as $function) {
if (isset($this->processedFunctions[$function])) {
continue;
}
$this->processFunctionOrMethod(new \ReflectionFunction($function));
$this->processedFunctions[$function] = true;
}
}
/**
* @param \ReflectionFunctionAbstract $functionOrMethod
*/
private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod)
{
if ($functionOrMethod->isInternal()) {
return;
}
$name = $functionOrMethod->getName();
if ($functionOrMethod instanceof \ReflectionMethod) {
$name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name;
}
if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) {
$this->lookupTable[$functionOrMethod->getFileName()] = [];
}
foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) {
$this->lookupTable[$functionOrMethod->getFileName()][$line] = $name;
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of resource-operations.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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',
];
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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, <null> 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];
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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<file>\\S+))', $lines[$i], $fromMatch) &&
\preg_match('(^\\+\\+\\+\\s+(?P<file>\\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<start>\d+)(?:,\s*(?P<startrange>\d+))?\s+\+(?P<end>\d+)(?:,\s*(?P<endrange>\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<type>[+ -])?(?P<line>.*)/', $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);
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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);
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
<?php
/*
* This file is part of the Finder Facade package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\FinderFacade;
use TheSeer\fDOM\fDOMDocument;
/**
* <code>
* <fileset>
* <include>
* <directory>/path/to/directory</directory>
* <file>/path/to/file</file>
* </include>
* <exclude>/path/to/directory</exclude>
* <name>*.php</name>
* </fileset>
* </code>
*
* @since Class available since Release 1.0.0
*/
class Configuration
{
/**
* @var string
*/
protected $basePath;
/**
* @var fDOMDocument
*/
protected $xml;
/**
* @param string $file
*/
public function __construct($file)
{
$this->basePath = dirname($file);
$this->xml = new fDOMDocument;
$this->xml->load($file);
}
/**
* @param string $xpath
*
* @return array
*/
public function parse($xpath = '')
{
$result = array(
'items' => array(),
'excludes' => array(),
'names' => array(),
'notNames' => array(),
'regularExpressionExcludes' => array()
);
foreach ($this->xml->getDOMXPath()->query($xpath . 'include/directory') as $item) {
$result['items'][] = $this->toAbsolutePath($item->nodeValue);
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'include/file') as $item) {
$result['items'][] = $this->toAbsolutePath($item->nodeValue);
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'exclude') as $exclude) {
$result['excludes'][] = $exclude->nodeValue;
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'name') as $name) {
$result['names'][] = $name->nodeValue;
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'notName') as $notName) {
$result['notNames'][] = $notName->nodeValue;
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'regularExpressionExcludes') as $regularExpressionExclude) {
$result['regularExpressionExcludes'][] = $regularExpressionExclude->nodeValue;
}
return $result;
}
/**
* @param string $path
*
* @return string
*/
protected function toAbsolutePath($path)
{
// Check whether the path is already absolute.
if ($path[0] === '/' || $path[0] === '\\' || (strlen($path) > 3 && ctype_alpha($path[0]) &&
$path[1] === ':' && ($path[2] === '\\' || $path[2] === '/'))) {
return $path;
}
// Check whether a stream is used.
if (strpos($path, '://') !== false) {
return $path;
}
return $this->basePath . DIRECTORY_SEPARATOR . $path;
}
}
<?php
/*
* This file is part of the Finder Facade package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\FinderFacade;
use Symfony\Component\Finder\Finder;
/**
* Convenience wrapper for Symfony's Finder component.
*
* @since Class available since Release 1.0.0
*/
class FinderFacade
{
/**
* @var array
*/
protected $items = array();
/**
* @var array
*/
protected $excludes = array();
/**
* @var array
*/
protected $names = array();
/**
* @var array
*/
protected $notNames = array();
/**
* @var array
*/
protected $regularExpressionsExcludes = array();
/**
* @param array $items
* @param array $excludes
* @param array $names
* @param array $notNames
* @param array $regularExpressionsExcludes
*/
public function __construct(array $items = array(), array $excludes = array(), array $names = array(), array $notNames = array(), $regularExpressionsExcludes = array())
{
$this->items = $items;
$this->excludes = $excludes;
$this->names = $names;
$this->notNames = $notNames;
$this->regularExpressionsExcludes = $regularExpressionsExcludes;
}
/**
* @return array
*/
public function findFiles()
{
$files = array();
$finder = new Finder;
$iterate = false;
$finder->ignoreUnreadableDirs();
foreach ($this->items as $item) {
if (!is_file($item)) {
$finder->in($item);
$iterate = true;
} else {
$files[] = realpath($item);
}
}
foreach ($this->excludes as $exclude) {
$finder->exclude($exclude);
}
foreach ($this->names as $name) {
$finder->name($name);
}
foreach ($this->notNames as $notName) {
$finder->notName($notName);
}
foreach ($this->regularExpressionsExcludes as $regularExpressionExclude) {
$finder->notPath($regularExpressionExclude);
}
if ($iterate) {
foreach ($finder as $file) {
$files[] = $file->getRealpath();
}
}
return $files;
}
/**
* @param string $file
*/
public function loadConfiguration($file)
{
$configuration = new Configuration($file);
$configuration = $configuration->parse();
$this->items = $configuration['items'];
$this->excludes = $configuration['excludes'];
$this->names = $configuration['names'];
$this->notNames = $configuration['notNames'];
$this->regularExpressionsExcludes = $configuration['regularExpressionExcludes'];
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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]);
}
}
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of exporter package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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.
*
* <code>
* <?php
* use SebastianBergmann\Exporter\Exporter;
*
* $exporter = new Exporter;
* print $exporter->export(new Exception);
* </code>
*/
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 int $indentation The indentation level of the 2nd+ line
*
* @return string
*/
public function export($value, $indentation = 0)
{
return $this->recursiveExport($value, $indentation);
}
/**
* @param array<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 '...'.
*
* @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.
*
* @return array
*/
public function toArray($value)
{
if (!\is_object($value)) {
return (array) $value;
}
$array = [];
foreach ((array) $value as $key => $val) {
// Exception traces commonly reference hundreds to thousands of
// objects currently loaded in memory. Including them in the result
// has a severe negative performance impact.
if ("\0Error\0trace" === $key || "\0Exception\0trace" === $key) {
continue;
}
// 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(.+)$/', (string) $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) {
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) && (float) ((int) $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(
'<lf>',
"\n",
\str_replace(
["\r\n", "\n\r", "\r", "\n"],
['\r\n<lf>', '\n\r<lf>', '\r<lf>', '\n<lf>'],
$value
)
) .
"'";
}
$whitespace = \str_repeat(' ', (int)(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);
}
}
<?php
/*
* This file is part of object-reflector.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace SebastianBergmann\ObjectReflector;
class ObjectReflector
{
/**
* @param object $object
*
* @return array
*
* @throws InvalidArgumentException
*/
public function getAttributes($object): array
{
if (!is_object($object)) {
throw new InvalidArgumentException;
}
$attributes = [];
$className = get_class($object);
foreach ((array) $object as $name => $value) {
$name = explode("\0", (string) $name);
if (count($name) === 1) {
$name = $name[0];
} else {
if ($name[1] !== $className) {
$name = $name[1] . '::' . $name[2];
} else {
$name = $name[2];
}
}
$attributes[$name] = $value;
}
return $attributes;
}
}
<?php
/*
* This file is part of object-reflector.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace SebastianBergmann\ObjectReflector;
interface Exception
{
}
<?php
/*
* This file is part of object-reflector.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace SebastianBergmann\ObjectReflector;
class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class ObjectType extends Type
{
/**
* @var TypeName
*/
private $className;
/**
* @var bool
*/
private $allowsNull;
public function __construct(TypeName $className, bool $allowsNull)
{
$this->className = $className;
$this->allowsNull = $allowsNull;
}
public function isAssignable(Type $other): bool
{
if ($this->allowsNull && $other instanceof NullType) {
return true;
}
if ($other instanceof self) {
if (0 === \strcasecmp($this->className->getQualifiedName(), $other->className->getQualifiedName())) {
return true;
}
if (\is_subclass_of($other->className->getQualifiedName(), $this->className->getQualifiedName(), true)) {
return true;
}
}
return false;
}
public function getReturnTypeDeclaration(): string
{
return ': ' . ($this->allowsNull ? '?' : '') . $this->className->getQualifiedName();
}
public function allowsNull(): bool
{
return $this->allowsNull;
}
public function className(): TypeName
{
return $this->className;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class GenericObjectType extends Type
{
/**
* @var bool
*/
private $allowsNull;
public function __construct(bool $nullable)
{
$this->allowsNull = $nullable;
}
public function isAssignable(Type $other): bool
{
if ($this->allowsNull && $other instanceof NullType) {
return true;
}
if (!$other instanceof ObjectType) {
return false;
}
return true;
}
public function getReturnTypeDeclaration(): string
{
return ': ' . ($this->allowsNull ? '?' : '') . 'object';
}
public function allowsNull(): bool
{
return $this->allowsNull;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class UnknownType extends Type
{
public function isAssignable(Type $other): bool
{
return true;
}
public function getReturnTypeDeclaration(): string
{
return '';
}
public function allowsNull(): bool
{
return true;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
abstract class Type
{
public static function fromValue($value, bool $allowsNull): self
{
$typeName = \gettype($value);
if ($typeName === 'object') {
return new ObjectType(TypeName::fromQualifiedName(\get_class($value)), $allowsNull);
}
$type = self::fromName($typeName, $allowsNull);
if ($type instanceof SimpleType) {
$type = new SimpleType($typeName, $allowsNull, $value);
}
return $type;
}
public static function fromName(string $typeName, bool $allowsNull): self
{
switch (\strtolower($typeName)) {
case 'callable':
return new CallableType($allowsNull);
case 'iterable':
return new IterableType($allowsNull);
case 'null':
return new NullType;
case 'object':
return new GenericObjectType($allowsNull);
case 'unknown type':
return new UnknownType;
case 'void':
return new VoidType;
case 'array':
case 'bool':
case 'boolean':
case 'double':
case 'float':
case 'int':
case 'integer':
case 'real':
case 'resource':
case 'resource (closed)':
case 'string':
return new SimpleType($typeName, $allowsNull);
default:
return new ObjectType(TypeName::fromQualifiedName($typeName), $allowsNull);
}
}
abstract public function isAssignable(Type $other): bool;
abstract public function getReturnTypeDeclaration(): string;
abstract public function allowsNull(): bool;
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class NullType extends Type
{
public function isAssignable(Type $other): bool
{
return !($other instanceof VoidType);
}
public function getReturnTypeDeclaration(): string
{
return '';
}
public function allowsNull(): bool
{
return true;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class TypeName
{
/**
* @var ?string
*/
private $namespaceName;
/**
* @var string
*/
private $simpleName;
public static function fromQualifiedName(string $fullClassName): self
{
if ($fullClassName[0] === '\\') {
$fullClassName = \substr($fullClassName, 1);
}
$classNameParts = \explode('\\', $fullClassName);
$simpleName = \array_pop($classNameParts);
$namespaceName = \implode('\\', $classNameParts);
return new self($namespaceName, $simpleName);
}
public static function fromReflection(\ReflectionClass $type): self
{
return new self(
$type->getNamespaceName(),
$type->getShortName()
);
}
public function __construct(?string $namespaceName, string $simpleName)
{
if ($namespaceName === '') {
$namespaceName = null;
}
$this->namespaceName = $namespaceName;
$this->simpleName = $simpleName;
}
public function getNamespaceName(): ?string
{
return $this->namespaceName;
}
public function getSimpleName(): string
{
return $this->simpleName;
}
public function getQualifiedName(): string
{
return $this->namespaceName === null
? $this->simpleName
: $this->namespaceName . '\\' . $this->simpleName;
}
public function isNamespaced(): bool
{
return $this->namespaceName !== null;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class IterableType extends Type
{
/**
* @var bool
*/
private $allowsNull;
public function __construct(bool $nullable)
{
$this->allowsNull = $nullable;
}
/**
* @throws RuntimeException
*/
public function isAssignable(Type $other): bool
{
if ($this->allowsNull && $other instanceof NullType) {
return true;
}
if ($other instanceof self) {
return true;
}
if ($other instanceof SimpleType) {
return \is_iterable($other->value());
}
if ($other instanceof ObjectType) {
try {
return (new \ReflectionClass($other->className()->getQualifiedName()))->isIterable();
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
// @codeCoverageIgnoreEnd
}
}
return false;
}
public function getReturnTypeDeclaration(): string
{
return ': ' . ($this->allowsNull ? '?' : '') . 'iterable';
}
public function allowsNull(): bool
{
return $this->allowsNull;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class SimpleType extends Type
{
/**
* @var string
*/
private $name;
/**
* @var bool
*/
private $allowsNull;
/**
* @var mixed
*/
private $value;
public function __construct(string $name, bool $nullable, $value = null)
{
$this->name = $this->normalize($name);
$this->allowsNull = $nullable;
$this->value = $value;
}
public function isAssignable(Type $other): bool
{
if ($this->allowsNull && $other instanceof NullType) {
return true;
}
if ($other instanceof self) {
return $this->name === $other->name;
}
return false;
}
public function getReturnTypeDeclaration(): string
{
return ': ' . ($this->allowsNull ? '?' : '') . $this->name;
}
public function allowsNull(): bool
{
return $this->allowsNull;
}
public function value()
{
return $this->value;
}
private function normalize(string $name): string
{
$name = \strtolower($name);
switch ($name) {
case 'boolean':
return 'bool';
case 'real':
case 'double':
return 'float';
case 'integer':
return 'int';
case '[]':
return 'array';
default:
return $name;
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class CallableType extends Type
{
/**
* @var bool
*/
private $allowsNull;
public function __construct(bool $nullable)
{
$this->allowsNull = $nullable;
}
/**
* @throws RuntimeException
*/
public function isAssignable(Type $other): bool
{
if ($this->allowsNull && $other instanceof NullType) {
return true;
}
if ($other instanceof self) {
return true;
}
if ($other instanceof ObjectType) {
if ($this->isClosure($other)) {
return true;
}
if ($this->hasInvokeMethod($other)) {
return true;
}
}
if ($other instanceof SimpleType) {
if ($this->isFunction($other)) {
return true;
}
if ($this->isClassCallback($other)) {
return true;
}
if ($this->isObjectCallback($other)) {
return true;
}
}
return false;
}
public function getReturnTypeDeclaration(): string
{
return ': ' . ($this->allowsNull ? '?' : '') . 'callable';
}
public function allowsNull(): bool
{
return $this->allowsNull;
}
private function isClosure(ObjectType $type): bool
{
return !$type->className()->isNamespaced() && $type->className()->getSimpleName() === \Closure::class;
}
/**
* @throws RuntimeException
*/
private function hasInvokeMethod(ObjectType $type): bool
{
try {
$class = new \ReflectionClass($type->className()->getQualifiedName());
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
// @codeCoverageIgnoreEnd
}
if ($class->hasMethod('__invoke')) {
return true;
}
return false;
}
private function isFunction(SimpleType $type): bool
{
if (!\is_string($type->value())) {
return false;
}
return \function_exists($type->value());
}
private function isObjectCallback(SimpleType $type): bool
{
if (!\is_array($type->value())) {
return false;
}
if (\count($type->value()) !== 2) {
return false;
}
if (!\is_object($type->value()[0]) || !\is_string($type->value()[1])) {
return false;
}
[$object, $methodName] = $type->value();
$reflector = new \ReflectionObject($object);
return $reflector->hasMethod($methodName);
}
private function isClassCallback(SimpleType $type): bool
{
if (!\is_string($type->value()) && !\is_array($type->value())) {
return false;
}
if (\is_string($type->value())) {
if (\strpos($type->value(), '::') === false) {
return false;
}
[$className, $methodName] = \explode('::', $type->value());
}
if (\is_array($type->value())) {
if (\count($type->value()) !== 2) {
return false;
}
if (!\is_string($type->value()[0]) || !\is_string($type->value()[1])) {
return false;
}
[$className, $methodName] = $type->value();
}
\assert(isset($className) && \is_string($className));
\assert(isset($methodName) && \is_string($methodName));
try {
$class = new \ReflectionClass($className);
if ($class->hasMethod($methodName)) {
$method = $class->getMethod($methodName);
return $method->isPublic() && $method->isStatic();
}
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new RuntimeException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
// @codeCoverageIgnoreEnd
}
return false;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/type.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Type;
final class VoidType extends Type
{
public function isAssignable(Type $other): bool
{
return $other instanceof self;
}
public function getReturnTypeDeclaration(): string
{
return ': void';
}
public function allowsNull(): bool
{
return false;
}
}
<?php
/*
* This file is part of the Version package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/environment.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/environment.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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';
}
}
}
<?php declare(strict_types=1);
/*
* This file is part of sebastian/environment.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<?php
/*
* This file is part of the Recursion Context package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\RecursionContext;
/**
*/
interface Exception
{
}
<?php
/*
* This file is part of the Recursion Context package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\RecursionContext;
/**
*/
final class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
<?php
/*
* This file is part of the Recursion Context package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\RecursionContext;
/**
* A context containing previously processed arrays and objects
* when recursively processing a value.
*/
final class Context
{
/**
* @var array[]
*/
private $arrays;
/**
* @var \SplObjectStorage
*/
private $objects;
/**
* Initialises the context
*/
public function __construct()
{
$this->arrays = array();
$this->objects = new \SplObjectStorage;
}
/**
* Adds a value to the context.
*
* @param array|object $value The value to add.
*
* @return int|string The ID of the stored value, either as a string or integer.
*
* @throws InvalidArgumentException Thrown if $value is not an array or object
*/
public function add(&$value)
{
if (is_array($value)) {
return $this->addArray($value);
} elseif (is_object($value)) {
return $this->addObject($value);
}
throw new InvalidArgumentException(
'Only arrays and objects are supported'
);
}
/**
* Checks if the given value exists within the context.
*
* @param array|object $value The value to check.
*
* @return int|string|false The string or integer ID of the stored value if it has already been seen, or false if the value is not stored.
*
* @throws InvalidArgumentException Thrown if $value is not an array or object
*/
public function contains(&$value)
{
if (is_array($value)) {
return $this->containsArray($value);
} elseif (is_object($value)) {
return $this->containsObject($value);
}
throw new InvalidArgumentException(
'Only arrays and objects are supported'
);
}
/**
* @param array $array
*
* @return bool|int
*/
private function addArray(array &$array)
{
$key = $this->containsArray($array);
if ($key !== false) {
return $key;
}
$key = count($this->arrays);
$this->arrays[] = &$array;
if (!isset($array[PHP_INT_MAX]) && !isset($array[PHP_INT_MAX - 1])) {
$array[] = $key;
$array[] = $this->objects;
} else { /* cover the improbable case too */
do {
$key = random_int(PHP_INT_MIN, PHP_INT_MAX);
} while (isset($array[$key]));
$array[$key] = $key;
do {
$key = random_int(PHP_INT_MIN, PHP_INT_MAX);
} while (isset($array[$key]));
$array[$key] = $this->objects;
}
return $key;
}
/**
* @param object $object
*
* @return string
*/
private function addObject($object)
{
if (!$this->objects->contains($object)) {
$this->objects->attach($object);
}
return spl_object_hash($object);
}
/**
* @param array $array
*
* @return int|false
*/
private function containsArray(array &$array)
{
$end = array_slice($array, -2);
return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false;
}
/**
* @param object $value
*
* @return string|false
*/
private function containsObject($value)
{
if ($this->objects->contains($value)) {
return spl_object_hash($value);
}
return false;
}
public function __destruct()
{
foreach ($this->arrays as &$array) {
if (is_array($array)) {
array_pop($array);
array_pop($array);
}
}
}
}
<?php
/*
* This file is part of Object Enumerator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\ObjectEnumerator;
interface Exception
{
}
<?php
/*
* This file is part of Object Enumerator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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
{
}
<?php
/*
* This file is part of Object Enumerator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* 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;
}
}
<99>ˬ<9B><B5><81><81>8@<40><>+U<17><>a<EA><61><93><F0>o<EB>'<27><>A<UmU<6D>q<FB>ȹΦo<CEA6>c<C9>\<5C><>=++<14>3<BE><16><>Ǚ<BB>GBMB